From dee2cf0d0467275e735b959dc0c65fff185b7985 Mon Sep 17 00:00:00 2001 From: Alessandro Di Federico Date: Tue, 16 Oct 2018 16:15:07 +0200 Subject: [PATCH] Reimplement the reaching definition analyses This commit reimplements the (conditional) reaching definitions passes as an instance of a monotone framework. The `ConditionNumberingPass` has also been reworked in the way it exposes its results, but it's otherwise unchanged. A proper unit testing framework is also available to ensure everything works as supposed to. --- .../BasicAnalyses/GeneratedCodeBasicInfo.h | 2 +- .../ReachingDefinitionsAnalysisImpl.h | 442 ++++++ .../BasicAnalyses/ReachingDefinitionsPass.h | 230 ++++ include/revng/Support/IRHelpers.h | 13 +- .../revng/Support}/MemoryAccess.h | 0 lib/BasicAnalyses/CMakeLists.txt | 7 +- lib/BasicAnalyses/ReachingDefinitionsPass.cpp | 429 ++++++ scripts/check-conventions.sh | 5 +- tests/Unit/ReachingDefinitionsPass.cpp | 530 ++++++++ tests/Unit/UnitTests.cmake | 29 + tools/revamb/CMakeLists.txt | 1 - tools/revamb/InstructionTranslator.h | 1 + tools/revamb/JumpTargetManager.cpp | 1 + tools/revamb/NoReturnAnalysis.h | 4 +- tools/revamb/OSRA.cpp | 5 +- tools/revamb/OSRA.h | 2 +- tools/revamb/ReachingDefinitionsPass.cpp | 1183 ----------------- tools/revamb/ReachingDefinitionsPass.h | 391 ------ tools/revamb/SimplifyComparisonsPass.cpp | 1 + tools/revamb/SimplifyComparisonsPass.h | 4 +- 20 files changed, 1692 insertions(+), 1588 deletions(-) create mode 100644 include/revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h create mode 100644 include/revng/BasicAnalyses/ReachingDefinitionsPass.h rename {tools/revamb => include/revng/Support}/MemoryAccess.h (100%) create mode 100644 lib/BasicAnalyses/ReachingDefinitionsPass.cpp create mode 100644 tests/Unit/ReachingDefinitionsPass.cpp delete mode 100644 tools/revamb/ReachingDefinitionsPass.cpp delete mode 100644 tools/revamb/ReachingDefinitionsPass.h diff --git a/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h b/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h index ee848c852..c43ce24ff 100644 --- a/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h +++ b/include/revng/BasicAnalyses/GeneratedCodeBasicInfo.h @@ -266,7 +266,7 @@ template<> struct BlackListTrait : BlackListTraitBase { using BlackListTraitBase::BlackListTraitBase; - bool isBlacklisted(llvm::BasicBlock *Value) { + bool isBlacklisted(llvm::BasicBlock *Value) const { return !this->Obj.isTranslated(Value); } }; diff --git a/include/revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h b/include/revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h new file mode 100644 index 000000000..3ae29016d --- /dev/null +++ b/include/revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h @@ -0,0 +1,442 @@ +#ifndef REACHINGDEFINITIONSANALYSISIMPL_H +#define REACHINGDEFINITIONSANALYSISIMPL_H + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +// LLVM includes +#include "llvm/ADT/SmallVector.h" + +// Local libraries includes +#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" +#include "revng/Support/MemoryAccess.h" +#include "revng/Support/MonotoneFramework.h" + +struct MemoryInstruction { + MemoryInstruction() : I(nullptr), MA() {} + MemoryInstruction(llvm::StoreInst *I, const llvm::DataLayout &DL) : + I(I), + MA(I, DL) {} + MemoryInstruction(llvm::LoadInst *I, const llvm::DataLayout &DL) : + I(I), + MA(I, DL) {} + + template + static MemoryInstruction + create(llvm::StoreInst *I, const llvm::DataLayout &DL, const T &Container) { + MemoryInstruction Result(I, DL); + for (int32_t Color : Container) + Result.Colors.push_back(Color); + return Result; + } + + template + static MemoryInstruction + create(llvm::LoadInst *I, const llvm::DataLayout &DL, const T &Container) { + MemoryInstruction Result(I, DL); + for (int32_t Color : Container) + Result.Colors.push_back(Color); + return Result; + } + + bool operator<(const MemoryInstruction Other) const { return I < Other.I; } + bool operator>(const MemoryInstruction Other) const { return I > Other.I; } + bool operator==(const MemoryInstruction Other) const { return I == Other.I; } + + llvm::Instruction *I; + MemoryAccess MA; + llvm::SmallVector Colors; +}; + +/// \brief Normalize the graph: indirect branch successors must have only one +/// predecessor +inline std::set highlightConditionEdges(llvm::Function &F) { + using namespace llvm; + + LLVMContext &C = getContext(&F); + + std::set ToDelete; + for (BasicBlock &BB : F) { + auto *T = dyn_cast(BB.getTerminator()); + if (T == nullptr or T->isUnconditional()) + continue; + + std::array SuccessorsUses{ &T->getOperandUse(1), + &T->getOperandUse(2) }; + for (Use *SuccessorUse : SuccessorsUses) { + BasicBlock *Successor = cast(SuccessorUse->get()); + + // Check if the successor has more than one predecessor + if (Successor->getSinglePredecessor() == &BB) + continue; + + // Create a new basic block, set it as successor of T + auto *NewBB = BasicBlock::Create(C, "", &F); + ToDelete.insert(NewBB); + SuccessorUse->set(NewBB); + + // Add NewBB -> Successor branch + BranchInst::Create(Successor, NewBB); + } + } + + return ToDelete; +} + +namespace RDA { + +using ColorsList = llvm::SmallVector; +using MISet = MonotoneFrameworkSet; + +class Interrupt { +private: + enum InterruptType { Regular, Summary, NoReturn }; + +private: + InterruptType Type; + MISet E; + +private: + Interrupt(InterruptType Type) : Type(Type) { revng_assert(Type != Regular); } + + Interrupt(InterruptType Type, MISet E) : Type(Type), E(E) { + revng_assert(Type == Regular); + } + +public: + static Interrupt createRegular(MISet E) { return Interrupt(Regular, E); } + + static Interrupt createNoReturn() { return Interrupt(NoReturn); } + + static Interrupt createSummary() { return Interrupt(Summary); } + + bool requiresInterproceduralHandling() { + switch (Type) { + case Regular: + return false; + case Summary: + case NoReturn: + return true; + } + + revng_abort(); + } + + MISet &&extractResult() { return std::move(E); } + bool isReturn() const { return false; } +}; + +template +struct ColorsProviderTraits {}; + +class NullColorsProvider {}; + +extern ColorsList EmptyColorsList; +extern llvm::SmallVector EmptyResetColorsList; + +template<> +struct ColorsProviderTraits { + + static const ColorsList & + getBlockColors(const NullColorsProvider &CP, llvm::BasicBlock *BB) { + return EmptyColorsList; + } + + static int32_t getEdgeColor(const NullColorsProvider &CP, + llvm::BasicBlock *Source, + llvm::BasicBlock *Destination) { + return 0; + } + + static const llvm::SmallVector & + getResetColors(const NullColorsProvider &CP, llvm::BasicBlock *BB) { + return EmptyResetColorsList; + } +}; + +extern llvm::SmallVector EmtpyReachersList; + +template +inline const GeneratedCodeBasicInfo *getGCBIOrNull(const T &Obj) { + return nullptr; +} + +template<> +inline const GeneratedCodeBasicInfo * +getGCBIOrNull(const GeneratedCodeBasicInfo &GCBI) { + return &GCBI; +} + +template +class Analysis + : public MonotoneFramework, + llvm::SmallVector, + ReversePostOrder> { +public: + using SuccessorsList = llvm::SmallVector; + +private: + using Base = MonotoneFramework, + SuccessorsList, + ReversePostOrder>; + +private: + /// The function to analyze + llvm::Function *F; + + /// Map for the results: records all the reaching definitions + std::map> + ReachedBy; + + /// Map of colors associated to a basic block + const ColorsProvider &TheColorsProvider; + + /// Trait to query a blacklist + BlackListTrait TheBlackList; + + const GeneratedCodeBasicInfo *GCBI; + +public: + Analysis(llvm::Function *F, + const ColorsProvider &TheColorsProvider, + const BlackList &TheBlackList) : + Base(&F->getEntryBlock()), + F(F), + TheColorsProvider(TheColorsProvider), + TheBlackList(TheBlackList) { + + GCBI = getGCBIOrNull(TheBlackList); + } + + std::map> && + extractResults() { + return std::move(ReachedBy); + } + + void initialize() { + ReachedBy.clear(); + Base::initialize(); + } + + void assertLowerThanOrEqual(const MISet &A, const MISet &B) const {} + + void dumpFinalState() const {} + + SuccessorsList successors(llvm::BasicBlock *BB, Interrupt &) const { + SuccessorsList Result; + for (llvm::BasicBlock *Successor : make_range(succ_begin(BB), succ_end(BB))) + Result.push_back(Successor); + return Result; + } + + size_t successor_size(llvm::BasicBlock *BB, Interrupt &I) const { + return succ_end(BB) - succ_begin(BB); + } + + Interrupt createSummaryInterrupt() { return Interrupt::createSummary(); } + + Interrupt createNoReturnInterrupt() const { + return Interrupt::createNoReturn(); + } + + MISet extremalValue(llvm::BasicBlock *) const { return MISet(); } + + typename Base::LabelRange extremalLabels() const { + return { &F->getEntryBlock() }; + } + + const ColorsList &getBlockColors(llvm::BasicBlock *BB) const { + using CP = ColorsProviderTraits; + return CP::getBlockColors(TheColorsProvider, BB); + } + + int32_t + getEdgeColor(llvm::BasicBlock *Source, llvm::BasicBlock *Destination) const { + return ColorsProviderTraits::getEdgeColor(TheColorsProvider, + Source, + Destination); + } + + const llvm::SmallVector & + getResetColors(llvm::BasicBlock *BB) const { + using CP = ColorsProviderTraits; + return CP::getResetColors(TheColorsProvider, BB); + } + + llvm::Optional handleEdge(const MISet &Original, + llvm::BasicBlock *Source, + llvm::BasicBlock *Destination) const { + using namespace llvm; + + // Is the destination blacklisted? + if (TheBlackList.isBlacklisted(Destination)) + return { MISet() }; + + // Is the destination painted with a color that is opposite to one of those + // where it has been defined? + int32_t EdgeColor = getEdgeColor(Source, Destination); + if (EdgeColor == 0) + return Optional(); + + MISet Filtered = Original; + + using MI = MemoryInstruction; + auto HasOppositeColors = [EdgeColor](const MI &Other) { + for (int32_t DefiningColor : Other.Colors) + if (DefiningColor == -EdgeColor) + return true; + return false; + }; + Filtered.erase_if(HasOppositeColors); + bool Changed = Filtered.size() != Original.size(); + + llvm::SmallVector ToInsert; + for (auto It = Filtered.begin(); It != Filtered.end();) { + + // TODO: this is suboptimal + // Check if this MI has EdgeColor + auto ColorIt = std::find(It->Colors.begin(), It->Colors.end(), EdgeColor); + if (ColorIt == It->Colors.end()) { + // Prepare new entry adding EdgeColor + MemoryInstruction Clone = *It; + Clone.Colors.push_back(EdgeColor); + ToInsert.push_back(Clone); + + // Delete old entry + It = Filtered.erase(It); + + Changed = true; + } else { + It++; + } + } + + for (MemoryInstruction &MI : ToInsert) + Filtered.insert(MI); + + // If something changed, return the updated version + if (Changed) + return { Filtered }; + + // Returning an empty optional means Original will be used as is + return Optional(); + } + + Interrupt transfer(llvm::BasicBlock *BB) { + using namespace llvm; + + MISet AliveMIs = this->State[BB]; + + // Remove the colors that need to be reset in this basic block + const llvm::SmallVector &ResetColors = getResetColors(BB); + + llvm::SmallVector ToInsert; + for (auto It = AliveMIs.begin(); It != AliveMIs.end();) { + + // Clone and drop all reset colors + MemoryInstruction Clone = *It; + auto IsResetColor = [&ResetColors](int32_t Color) { + auto It = std::find(ResetColors.begin(), ResetColors.end(), Color); + return It != ResetColors.end(); + }; + Clone.Colors.erase(std::remove_if(Clone.Colors.begin(), + Clone.Colors.end(), + IsResetColor), + Clone.Colors.end()); + + // Did we remove at least one color? + if (Clone.Colors.size() != It->Colors.size()) { + // Remove and register for insertion + It = AliveMIs.erase(It); + ToInsert.push_back(Clone); + } else { + // Proceed + It++; + } + } + + for (MemoryInstruction &MI : ToInsert) + AliveMIs.insert(MI); + + const DataLayout &DL = getModule(BB)->getDataLayout(); + + for (Instruction &I : *BB) { + MemoryInstruction MI; + auto *Load = dyn_cast(&I); + auto *Store = dyn_cast(&I); + auto MayAlias = [&MI](const MemoryInstruction &Other) { + return MI.MA.mayAlias(Other.MA); + }; + + if (Load != nullptr) { + Value *Pointer = Load->getPointerOperand(); + if ((not isa(Pointer) and not isa(Pointer)) + or Pointer->getName() == "env") + continue; + + MI = MemoryInstruction::create(Load, DL, getBlockColors(BB)); + + // Register all the reachers + SmallVector &Reachers = ReachedBy[Load]; + Reachers.clear(); + for (const MemoryInstruction &AliveMI : AliveMIs) + if (AliveMI.I != MI.I and AliveMI.MA.mayAlias(MI.MA)) + Reachers.push_back(AliveMI.I); + + // If no other instruction is writing in the load address, register this + // instruction as alive + if (not AliveMIs.contains(MayAlias)) + AliveMIs.insert(MI); + + } else if (Store != nullptr) { + Value *Pointer = Store->getPointerOperand(); + if ((not isa(Pointer) and not isa(Pointer)) + or Pointer->getName() == "env") + continue; + + MI = MemoryInstruction::create(Store, DL, getBlockColors(BB)); + + // Erase all the instruction clobbered by this store + AliveMIs.erase_if(MayAlias); + + // Insert the store instruction among the alive instructions + AliveMIs.insert(MI); + } + } + + // Don't follow function calls for now + if (GCBI != nullptr && GCBI->isFunctionCall(BB)) + return Interrupt::createRegular(MISet()); + + // TODO: this is an hack and should be replaced once we integrate calling + // convention and call graph in the basic block harvesting process + unsigned SuccessorsCount = succ_end(BB) - succ_begin(BB); + unsigned Size = AliveMIs.size(); + if (Size * SuccessorsCount > 5000) + return Interrupt::createRegular(MISet()); + + return Interrupt::createRegular(std::move(AliveMIs)); + } + +public: + const llvm::SmallVector & + getReachers(llvm::LoadInst *I) const { + auto It = ReachedBy.find(I); + if (It == ReachedBy.end()) + return EmtpyReachersList; + else + return It->second; + } +}; + +} // namespace RDA + +#endif // REACHINGDEFINITIONSANALYSISIMPL_H diff --git a/include/revng/BasicAnalyses/ReachingDefinitionsPass.h b/include/revng/BasicAnalyses/ReachingDefinitionsPass.h new file mode 100644 index 000000000..1acc94d03 --- /dev/null +++ b/include/revng/BasicAnalyses/ReachingDefinitionsPass.h @@ -0,0 +1,230 @@ +#ifndef REACHINGDEFINITIONSPASS_H +#define REACHINGDEFINITIONSPASS_H + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +// Standard includes +#include + +// LLVM includes +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Dominators.h" +#include "llvm/Pass.h" + +// Local libraries includes +#include "revng/BasicAnalyses/FunctionCallIdentification.h" +#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" + +extern llvm::SmallVector EmptyReachingDefinitionsList; + +class ReachingDefinitionsPass : public llvm::FunctionPass { +public: + using ReachingDefinitionsVector = llvm::SmallVector; + +public: + static char ID; + + ReachingDefinitionsPass() : llvm::FunctionPass(ID){}; + ReachingDefinitionsPass(char &ID) : llvm::FunctionPass(ID){}; + + bool runOnFunction(llvm::Function &F) override; + + virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired(); + AU.addRequired(); + } + + const ReachingDefinitionsVector & + getReachingDefinitions(llvm::LoadInst *Load) const { + auto It = ReachingDefinitions.find(Load); + if (It == ReachingDefinitions.end()) + return EmptyReachingDefinitionsList; + else + return It->second; + } + + virtual void releaseMemory() override { + revng_log(ReleaseLog, "ReachingDefinitionsPass is releasing memory"); + freeContainer(ReachingDefinitions); + } + +private: + std::map ReachingDefinitions; +}; + +/// The ConditionNumberingPass loops over all the conditional branch +/// instructions in the program and tries to identify those that are based on +/// exactly the same condition, i.e., the pair for which can be sure that, if +/// the first branch is taken, then also the second branch will be taken. This +/// is particularly useful to handle consecutive predicate instructions. +/// +/// Two conditions are considered the same, if they actually are the same or if +/// they compute exactly the same operations on the same operands. To +/// efficiently identify which branch instructions use the same conditions we +/// populate an hashmap with a custom hash function. At the end, we will discard +/// all the entries of the hashmap with a single entry, since we're not +/// interested in considering a condition if it doesn't have at least a +/// companion branch instruction. Each condition with at least two branches +/// using it is assigned a unique identifier, the condition index. +/// +/// The ConditionNumberingPass also provides, for each condition index, a list +/// of "reset" basic blocks, i.e., a list of basic blocks which define at least +/// one of the values involved in the computation of the condition. Such a list +/// can be used to understand when it doesn't make sense for an analysis to +/// consider that a certain condition is still holding. +/// +/// "reset" basic blocks also include the last basic block that might be +/// affected by the associated condition index. This is useful to prevent an +/// analysis from keeping track of a condition index which we can be sure will +/// never be used again. The last basic block that might be affected by a +/// condition index is the immediate post-dominator of the set of basic blocks +/// containing the branches associated to that condition index. +/// +/// The following figures examplifies the situation: BB1 and BB2 share the same +/// condition, BB3 is their immediate post-dominator. To easily identify it as +/// such we introduce a temporary basic block BB0 and make it a predecessor of +/// both BB1 and BB2. Then, we compute the post-dominator tree and ask for the +/// immediate post-domiantor of BB0, obtaining BB3. +/// +/// +-----------+ +/// | | +/// +- - - - - -+ BB0 +- - - - -+ +/// | | | | +/// +-----------+ +/// | | +/// +/// +-----v-----+ +-----v-----+ +/// | | | | +/// +---+ BB1 +---+ +---+ BB2 +---+ +/// | | | | | | | | +/// | +-----------+ | | +-----------+ | +/// | | | | +/// | | | | +/// +-----v-----+ +-----v-----+ +-----v-----+ +-----v-----+ +/// | | | | | | | | +/// | | | | | | | | +/// | | | | | | | | +/// +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ +/// | | | | +/// | | | | +/// | +-----v-----+ | +-----v-----+ +/// | | | | | | +/// +-------------> <-------+ | | +/// | | | | +/// +-----+-----+ +-----+-----+ +/// | | +/// | | +/// | +-----------+ | +/// | | | | +/// +----------> BB3 <----------+ +/// | | +/// +-----+-----+ +/// | +/// | +/// v +class ConditionNumberingPass : public llvm::FunctionPass { +public: + static char ID; + + static const llvm::SmallVector NoDefinedConditions; + + ConditionNumberingPass() : llvm::FunctionPass(ID){}; + + bool runOnFunction(llvm::Function &F) override; + + void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.addRequired(); + AU.setPreservesAll(); + } + + const llvm::SmallVector *getColors(llvm::BasicBlock *BB) const { + auto It = Colors.find(BB); + if (It == Colors.end()) + return nullptr; + else + return &It->second; + } + + int32_t + getEdgeColor(llvm::BasicBlock *Source, llvm::BasicBlock *Destination) const { + auto It = EdgeColors.find({ Source, Destination }); + if (It == EdgeColors.end()) + return 0; + else + return It->second; + } + + const llvm::SmallVector * + getResetColors(llvm::BasicBlock *BB) const { + auto It = ResetColors.find(BB); + if (It == ResetColors.end()) + return &It->second; + else + return nullptr; + } + + virtual void releaseMemory() override { + revng_log(ReleaseLog, "ConditionNumberingPass is releasing memory"); + freeContainer(DefinedConditions); + freeContainer(BranchConditionNumberMap); + freeContainer(Colors); + } + +private: + using BasicBlock = llvm::BasicBlock; + std::map> DefinedConditions; + std::map, int32_t> EdgeColors; + std::map BranchConditionNumberMap; + std::map> ResetColors; + + using ColorsList = llvm::SmallVector; + using ColorMap = std::map; + ColorMap Colors; +}; + +class ConditionalReachedLoadsPass : public llvm::FunctionPass { +public: + using ReachingDefinitionsVector = llvm::SmallVector; + +public: + static char ID; + + ConditionalReachedLoadsPass() : llvm::FunctionPass(ID){}; + + bool runOnFunction(llvm::Function &F) override; + + virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { + AU.setPreservesAll(); + AU.addRequired(); + AU.addRequired(); + AU.addRequired(); + } + + virtual void releaseMemory() override { + revng_log(ReleaseLog, "ConditionalReachedLoadsPass is releasing memory"); + freeContainer(ReachedLoads); + freeContainer(ReachingDefinitions); + } + + const ReachingDefinitionsVector & + getReachingDefinitions(llvm::LoadInst *Load) const { + auto It = ReachingDefinitions.find(Load); + if (It == ReachingDefinitions.end()) + return EmptyReachingDefinitionsList; + else + return It->second; + } + + const llvm::SmallVector & + getReachedLoads(const llvm::Instruction *I) const; + +private: + using ReachedLoadsVector = llvm::SmallVector; + std::map ReachedLoads; + std::map ReachingDefinitions; +}; + +#endif // REACHINGDEFINITIONSPASS_H diff --git a/include/revng/Support/IRHelpers.h b/include/revng/Support/IRHelpers.h index bd814a9ce..f918f68cf 100644 --- a/include/revng/Support/IRHelpers.h +++ b/include/revng/Support/IRHelpers.h @@ -178,17 +178,26 @@ protected: template struct BlackListTrait : BlackListTraitBase {}; +class NullBlackList {}; + +template +struct BlackListTrait + : BlackListTraitBase { + using BlackListTraitBase::BlackListTraitBase; + bool isBlacklisted(B Value) const { return false; } +}; + template struct BlackListTrait : BlackListTraitBase { using BlackListTraitBase::BlackListTraitBase; - bool isBlacklisted(C Value) { return Value == this->Obj; } + bool isBlacklisted(C Value) const { return Value == this->Obj; } }; template struct BlackListTrait &, B> : BlackListTraitBase &> { using BlackListTraitBase &>::BlackListTraitBase; - bool isBlacklisted(B Value) { return this->Obj.count(Value) != 0; } + bool isBlacklisted(B Value) const { return this->Obj.count(Value) != 0; } }; template diff --git a/tools/revamb/MemoryAccess.h b/include/revng/Support/MemoryAccess.h similarity index 100% rename from tools/revamb/MemoryAccess.h rename to include/revng/Support/MemoryAccess.h diff --git a/lib/BasicAnalyses/CMakeLists.txt b/lib/BasicAnalyses/CMakeLists.txt index 1a36e8805..9a6f30a25 100644 --- a/lib/BasicAnalyses/CMakeLists.txt +++ b/lib/BasicAnalyses/CMakeLists.txt @@ -1 +1,6 @@ -add_library(BasicAnalyses STATIC GeneratedCodeBasicInfo.cpp FunctionCallIdentification.cpp) +add_library(BasicAnalyses + STATIC + FunctionCallIdentification.cpp + GeneratedCodeBasicInfo.cpp + ReachingDefinitionsPass.cpp) +target_link_libraries(BasicAnalyses Support) diff --git a/lib/BasicAnalyses/ReachingDefinitionsPass.cpp b/lib/BasicAnalyses/ReachingDefinitionsPass.cpp new file mode 100644 index 000000000..5b4f78d04 --- /dev/null +++ b/lib/BasicAnalyses/ReachingDefinitionsPass.cpp @@ -0,0 +1,429 @@ +/// \file ReachingDefinitionsPass.cpp +/// \brief + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +// LLVM includes +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/BasicBlock.h" + +// Local libraries includes +#include "revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h" +#include "revng/BasicAnalyses/ReachingDefinitionsPass.h" +#include "revng/Support/IRHelpers.h" +#include "revng/Support/Statistics.h" + +using namespace llvm; + +using std::pair; +using std::queue; +using std::tie; +using std::unordered_map; + +static Logger<> RDPLog("rdp"); +static Logger<> CNPLog("cnp"); + +static RunningStatistics RDAStats("RDAStats"); + +static SmallVector EmptyReachedLoadsList; +SmallVector EmptyReachingDefinitionsList; +SmallVector EmptyResetColorsList; + +char ReachingDefinitionsPass::ID = 0; +char ConditionalReachedLoadsPass::ID = 0; +char ConditionNumberingPass::ID = 0; + +namespace { + +using RegisterRDP = RegisterPass; +using RegisterCRLP = RegisterPass; +using RegisterCNP = RegisterPass; + +RegisterRDP W("rdp", "Reaching Definitions Pass", true, true); +RegisterCRLP Y("crlp", "Conditional Reached Loads Pass", true, true); +RegisterCNP Z("cnp", "Condition Numbering Pass", true, true); + +} // namespace + +using IndexesVector = SmallVector; +const IndexesVector ConditionNumberingPass::NoDefinedConditions; + +namespace RDA { + +SmallVector EmtpyReachersList; +ColorsList EmptyColorsList; +SmallVector EmptyResetColorsList; + +template<> +struct ColorsProviderTraits { + static const ColorsList & + getBlockColors(const ConditionNumberingPass &CNP, llvm::BasicBlock *BB) { + const ColorsList *Result = CNP.getColors(BB); + if (Result == nullptr) + return EmptyColorsList; + else + return *Result; + } + + static int32_t getEdgeColor(const ConditionNumberingPass &CNP, + llvm::BasicBlock *Source, + llvm::BasicBlock *Destination) { + return CNP.getEdgeColor(Source, Destination); + } + + static const llvm::SmallVector & + getResetColors(const ConditionNumberingPass &CNP, llvm::BasicBlock *BB) { + const SmallVector *Result = CNP.getResetColors(BB); + if (Result == nullptr) + return EmptyResetColorsList; + else + return *Result; + } +}; + +} // namespace RDA + +bool ReachingDefinitionsPass::runOnFunction(llvm::Function &F) { + revng_log(PassesLog, "Starting ReachingDefinitionsPass"); + + using Analysis = RDA::Analysis; + Analysis A(&F, + RDA::NullColorsProvider(), + this->getAnalysis()); + A.registerExtremal(&F.getEntryBlock()); + A.initialize(); + A.run(); + + ReachingDefinitions = A.extractResults(); + + revng_log(PassesLog, "Ending ReachingDefinitionsPass"); + + return false; +} + +const SmallVector & +ConditionalReachedLoadsPass::getReachedLoads(const Instruction *I) const { + auto It = ReachedLoads.find(I); + if (It == ReachedLoads.end()) + return EmptyReachedLoadsList; + else + return It->second; +} + +bool ConditionalReachedLoadsPass::runOnFunction(llvm::Function &F) { + revng_log(PassesLog, "Starting ConditionalReachedLoadsPass"); + + using Analysis = RDA::Analysis; + Analysis A(&F, + this->getAnalysis(), + this->getAnalysis()); + A.registerExtremal(&F.getEntryBlock()); + A.initialize(); + A.run(); + + ReachingDefinitions = std::move(A.extractResults()); + + auto GetOperand = [](Instruction *I) { + if (auto *Store = dyn_cast(I)) + return Store->getPointerOperand()->getName().data(); + else if (auto *Load = dyn_cast(I)) + return Load->getPointerOperand()->getName().data(); + revng_abort(); + }; + + // Invert the map too + RDAStats.clear(); + ReachedLoads.clear(); + for (auto &P : ReachingDefinitions) { + LoadInst *Load = P.first; + ReachingDefinitionsVector &RDV = P.second; + + if (RDPLog.isEnabled()) { + RDPLog << getName(Load) << " (" << GetOperand(Load) + << ") is reached by:\n"; + } + RDAStats.push(RDV.size()); + + for (Instruction *Definition : RDV) { + if (RDPLog.isEnabled()) { + RDPLog << " " << getName(Definition) << " (" << GetOperand(Definition) + << ")\n"; + } + ReachedLoads[Definition].push_back(Load); + } + + RDPLog << DoLog; + } + + revng_log(PassesLog, "Ending ConditionalReachedLoadsPass"); + + return false; +} + +static size_t combine(size_t A, size_t B) { + return (A << 1 | A >> 31) ^ B; +} + +static size_t combine(size_t A, void *Ptr) { + return combine(A, reinterpret_cast(Ptr)); +} + +static bool isSupportedOperator(unsigned Opcode) { + switch (Opcode) { + case Instruction::Xor: + case Instruction::And: + case Instruction::Or: + case Instruction::ICmp: + return true; + default: + return false; + } +} + +class ConditionHash { +public: + ConditionHash(ReachingDefinitionsPass &RDP) : RDP(RDP) {} + + size_t operator()(BranchInst *const &V) const; + +private: + ReachingDefinitionsPass &RDP; +}; + +size_t ConditionHash::operator()(BranchInst *const &B) const { + Value *V = B->getCondition(); + size_t Hash = 0; + queue WorkList; + WorkList.push(V); + while (!WorkList.empty()) { + Value *V; + V = WorkList.front(); + WorkList.pop(); + + bool IsStore = isa(V); + bool IsLoad = isa(V); + if (IsStore || IsLoad) { + // Load/store vs load/store + if (IsStore) { + Hash = combine(Hash, cast(V)); + } else { + for (Instruction *I : RDP.getReachingDefinitions(cast(V))) { + if (auto *Store = dyn_cast(I)) + Hash = combine(Hash, Store); + else if (auto *Load = dyn_cast(I)) + Hash = combine(Hash, Load); + } + } + } else if (auto *I = dyn_cast(V)) { + // Instruction + if (!isSupportedOperator(I->getOpcode())) { + Hash = combine(Hash, V); + } else { + Hash = combine(Hash, I->getOpcode()); + Hash = combine(Hash, I->getNumOperands()); + for (unsigned C = 0; C < I->getNumOperands(); C++) + WorkList.push(I->getOperand(C)); + } + } else { + Hash = combine(Hash, V); + } + } + + return Hash; +} + +class ConditionEqualTo { +public: + ConditionEqualTo(ReachingDefinitionsPass &RDP) : RDP(RDP) {} + + bool operator()(BranchInst *const &A, BranchInst *const &B) const; + +private: + ReachingDefinitionsPass &RDP; +}; + +using BranchRef = BranchInst *const &; +bool ConditionEqualTo::operator()(BranchRef BA, BranchRef BB) const { + Value *A = BA->getCondition(); + Value *B = BB->getCondition(); + queue> WorkList; + WorkList.push({ A, B }); + while (!WorkList.empty()) { + Value *AV, *BV; + tie(AV, BV) = WorkList.front(); + WorkList.pop(); + + // Early continue in case they're exactly the same value + if (AV == BV) + continue; + + bool AIsStore = isa(AV); + bool AIsLoad = isa(AV); + bool BIsStore = isa(BV); + bool BIsLoad = isa(BV); + if ((AIsStore || AIsLoad) && (BIsStore || BIsLoad)) { + // Load/store vs load/store + llvm::SmallVector AStores; + if (AIsStore) + AStores.push_back(cast(AV)); + else + AStores = RDP.getReachingDefinitions(cast(AV)); + + llvm::SmallVector BStores; + if (BIsStore) + BStores.push_back(cast(BV)); + else + BStores = RDP.getReachingDefinitions(cast(BV)); + + if (AStores != BStores) + return false; + } else if (auto *AI = dyn_cast(AV)) { + // Instruction + auto *BI = dyn_cast(BV); + if (BI == nullptr || AI->getOpcode() != BI->getOpcode() + || AI->getNumOperands() != BI->getNumOperands() + || !isSupportedOperator(AI->getOpcode())) + return false; + + for (unsigned I = 0; I < AI->getNumOperands(); I++) + WorkList.push({ AI->getOperand(I), BI->getOperand(I) }); + } else { + return false; + } + } + + return true; +} + +static SmallVector +computeResetBasicBlocks(const ReachingDefinitionsPass &RDP, BranchInst *B) { + std::set Result; + Value *V = B->getCondition(); + queue WorkList; + WorkList.push(V); + while (not WorkList.empty()) { + Value *V; + V = WorkList.front(); + WorkList.pop(); + + auto *Store = dyn_cast(V); + auto *Load = dyn_cast(V); + if (Store != nullptr or Load != nullptr) { + // Load/store vs load/store + if (Store != nullptr) + Result.insert(Store->getParent()); + else + for (Instruction *I : RDP.getReachingDefinitions(Load)) + Result.insert(I->getParent()); + } else if (auto *I = dyn_cast(V)) { + // Instruction + if (not isSupportedOperator(I->getOpcode())) + Result.insert(I->getParent()); + else + for (unsigned C = 0; C < I->getNumOperands(); C++) + WorkList.push(I->getOperand(C)); + } + } + + SmallVector ResultVector; + std::copy(Result.begin(), Result.end(), std::back_inserter(ResultVector)); + return ResultVector; +} + +bool ConditionNumberingPass::runOnFunction(Function &F) { + + revng_log(PassesLog, "Starting ConditionNumberingPass"); + + auto &RDP = getAnalysis(); + using cnp_hashmap = unordered_map, + ConditionHash, + ConditionEqualTo>; + cnp_hashmap Conditions(10, ConditionHash(RDP), ConditionEqualTo(RDP)); + + // Group conditions together + for (BasicBlock &BB : F) + if (auto *Branch = dyn_cast(BB.getTerminator())) + if (Branch->isConditional()) + Conditions[Branch].push_back(Branch); + + std::set ToDelete = highlightConditionEdges(F); + + // 0 is a reserved value, since it doesn't have a corresponding negative + // value + uint32_t ConditionIndex = 1; + + DominatorTree DT(F); + Colors.clear(); + + for (auto &P : Conditions) { + const SmallVector &Sisters = P.second; + + // Ignore all the conditions present in a single branch + if (Sisters.size() < 2) + continue; + + // Compute reset basic blocks + for (BasicBlock *BB : computeResetBasicBlocks(RDP, P.first)) + ResetColors[BB].push_back(ConditionIndex); + + if (CNPLog.isEnabled()) { + CNPLog << "ConditionIndex " << ConditionIndex << ":"; + for (BranchInst *B : Sisters) + CNPLog << " " << getName(B); + CNPLog << DoLog; + } + + for (BranchInst *T : Sisters) { + revng_assert(T->isConditional()); + + // 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() == T->getParent()); + + SmallVector Descendants; + DT.getDescendants(Successor, Descendants); + for (BasicBlock *Descendant : Descendants) + if (ToDelete.count(Descendant) == 0) + Colors[Descendant].push_back(ConditionIndex); + + if (ToDelete.count(Successor) != 0) + Successor = Successor->getSingleSuccessor(); + revng_assert(Successor != nullptr); + + EdgeColors[{ T->getParent(), Successor }] = ConditionIndex; + + ConditionIndex = -ConditionIndex; + } + } + + ConditionIndex++; + } + + // Purge all the blocks created by highlightConditionEdges + for (BasicBlock *BB : ToDelete) { + auto It = BB->begin(); + auto End = BB->end(); + revng_assert(It != End and isa(&*It)); + It++; + revng_assert(It == End); + + BasicBlock *Successor = BB->getSingleSuccessor(); + BasicBlock *Predecessor = BB->getSinglePredecessor(); + revng_assert(Successor != nullptr and Predecessor != nullptr); + + BB->replaceAllUsesWith(Successor); + BB->eraseFromParent(); + } + + revng_log(PassesLog, "Ending ConditionNumberingPass"); + + return false; +} diff --git a/scripts/check-conventions.sh b/scripts/check-conventions.sh index 5578a87ed..1b9680173 100755 --- a/scripts/check-conventions.sh +++ b/scripts/check-conventions.sh @@ -66,10 +66,13 @@ fi done # Things should never be at the end of a line - for REGEXP in '::' '<' 'RegisterPass.*>' '(' '} else' '\bopt\b.*>'; do + for REGEXP in '::' '<' 'RegisterPass.*>' '} else' '\bopt\b.*>'; do $GREP "$REGEXP\$" $FILES | cat done + # Parenthesis at the end of line (except for raw strings) + $GREP "(\$" $FILES | grep -v 'R"LLVM.*(' | cat + # Things should never be at the beginning of a line for REGEXP in '\.[^\.]' '\*>' '/[^/\*]' ':[^:\(]*)' '==' '\!=' '<[^<]' '>' '>=' '<=' '//\s*WIP'; do $GREP "^\s*$REGEXP" $FILES | cat diff --git a/tests/Unit/ReachingDefinitionsPass.cpp b/tests/Unit/ReachingDefinitionsPass.cpp new file mode 100644 index 000000000..cc1bb83aa --- /dev/null +++ b/tests/Unit/ReachingDefinitionsPass.cpp @@ -0,0 +1,530 @@ +/// \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" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" + +// Local libraries includes +#include "revng/BasicAnalyses/ReachingDefinitionsAnalysisImpl.h" + +using namespace llvm; + +static const char *ModuleBegin = R"LLVM( +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@rax = internal global i64 0 +@rdi = internal global i64 0 +@rsi = internal global i64 0 +@rbx = internal global i64 0 +@rcx = internal global i64 0 + +define void @main() { +)LLVM"; + +static const char *ModuleEnd = "\n}\n"; + +static std::string buildModule(const char *Body) { + std::string Result; + Result += ModuleBegin; + Result += Body; + Result += ModuleEnd; + return Result; +} + +static Instruction *instructionByName(Function *F, const char *Name) { + if (StringRef(Name).startswith("s:")) { + Name = Name + 2; + for (BasicBlock &BB : *F) + for (Instruction &I : BB) + if (auto *Store = dyn_cast(&I)) + if (Store->getValueOperand()->hasName() + and Store->getValueOperand()->getName() == Name) + return &I; + } else { + for (BasicBlock &BB : *F) + for (Instruction &I : BB) + if (I.hasName() and I.getName() == Name) + return &I; + } + + revng_abort("Couldn't find a Value with the requested name"); +} + +static BasicBlock *basicBlockByName(Function *F, const char *Name) { + revng_assert(F != nullptr); + + for (BasicBlock &BB : *F) + if (BB.hasName() and BB.getName() == Name) + return &BB; + + revng_abort("Couldn't find a Value with the requested name"); +} + +static std::unique_ptr loadModule(LLVMContext &C, const char *Body) { + std::string ModuleText = buildModule(Body); + SMDiagnostic Diagnostic; + using MB = MemoryBuffer; + std::unique_ptr Buffer = MB::getMemBuffer(StringRef(ModuleText)); + std::unique_ptr M = parseIR(Buffer.get()->getMemBufferRef(), + Diagnostic, + C); + + if (M.get() == nullptr) { + Diagnostic.print("revamb", dbgs()); + revng_abort(); + } + + return M; +} + +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 + +class Test { +public: + enum Type { Regular, Conditional, Both }; + +private: + LLVMContext &Context; + +public: + Test() : Context(getGlobalContext()) {} + + void + test(const char *Body, + std::vector>> Checks, + std::vector BlackList = {}, + Type T = Both) { + + std::unique_ptr M = loadModule(Context, 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); + 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); + CA.registerExtremal(&F->getEntryBlock()); + CA.initialize(); + CA.run(); + + for (auto &P : Checks) + assertReachers(F, CA, P.first, P.second); + } + } +}; + +BOOST_AUTO_TEST_CASE(OneStoreOneLoad) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax", { "s:zero" } } }); +} + +BOOST_AUTO_TEST_CASE(StoreToDifferentCSV) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax", { "s:zero" } } }); +} + +BOOST_AUTO_TEST_CASE(ClobberingStore) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax", { "s:one" } } }); +} + +BOOST_AUTO_TEST_CASE(LoadReachingAnotherLoad) { + Test X; + + // + // Load reaching another load + // + const char *Body = R"LLVM( + %load_rax1 = load i64, i64* @rax + %load_rax2 = load i64, i64* @rax + ret void +)LLVM"; + + X.test(Body, { { "load_rax2", { "load_rax1" } } }); +} + +BOOST_AUTO_TEST_CASE(MultipleLoadsReachingAnotherLoad) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax3", { "load_rax1" } } }); +} + +BOOST_AUTO_TEST_CASE(IfStatement) { + Test X; + + // + // 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"; + + X.test(If, { { "load_rax", { "s:storeone", "s:storetwo" } } }); + + // Now try again but inhibiting propgation to the end basic block + X.test(If, { { "load_rax", {} } }, { "end" }); +} + +BOOST_AUTO_TEST_CASE(Loop) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax", { "s:storeone", "s:storetwo" } } }); +} + +BOOST_AUTO_TEST_CASE(SelfReachingLoad) { + Test X; + + // + // 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"; + + X.test(Body, { { "load_rax", {} } }); +} + +BOOST_AUTO_TEST_CASE(RepeatedIfStatement) { + Test X; + + // + // 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"; + + X.test(RepeatedIf, + { { "load_three", { "s:storeone", "s:storetwo" } }, + { "load_four", { "s:storeone", "s:storetwo" } } }, + {}, + Test::Regular); + + X.test(RepeatedIf, + { { "load_three", { "s:storeone" } }, + { "load_four", { "s:storetwo" } } }, + {}, + Test::Conditional); +} + +BOOST_AUTO_TEST_CASE(ConditionalDefinition) { + Test X; + + // + // 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"; + + X.test(ConditionalDefinition, + { { "load_one", { "s:storeone" } }, + { "load_two", { "s:storezero" } } }, + {}, + Test::Conditional); +} + +BOOST_AUTO_TEST_CASE(LoopClobbering) { + Test X; + + // + // 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"; + + X.test(ConditionalDefinition, + { { "load_one", { "s:storezero" } } }, + {}, + Test::Conditional); +} diff --git a/tests/Unit/UnitTests.cmake b/tests/Unit/UnitTests.cmake index 151d97f77..8a98ffd2b 100644 --- a/tests/Unit/UnitTests.cmake +++ b/tests/Unit/UnitTests.cmake @@ -9,6 +9,10 @@ set(SRC "${CMAKE_SOURCE_DIR}/tests/Unit") set(Boost_ADDITIONAL_VERSIONS "1.63" "1.63.0") find_package(Boost 1.63.0 REQUIRED COMPONENTS unit_test_framework) +# +# test_lazysmallbitvector +# + add_executable(test_lazysmallbitvector "${SRC}/lazysmallbitvector.cpp") target_include_directories(test_lazysmallbitvector PRIVATE "${CMAKE_SOURCE_DIR}" @@ -21,6 +25,10 @@ target_link_libraries(test_lazysmallbitvector ${LLVM_LIBRARIES}) add_test(NAME test_lazysmallbitvector COMMAND test_lazysmallbitvector) +# +# test_stackanalysis +# + add_executable(test_stackanalysis "${SRC}/stackanalysis.cpp") target_include_directories(test_stackanalysis PRIVATE "${CMAKE_SOURCE_DIR}" @@ -36,6 +44,10 @@ target_link_libraries(test_stackanalysis ${LLVM_LIBRARIES}) add_test(NAME test_stackanalysis COMMAND test_stackanalysis) +# +# test_classsentinel +# + add_executable(test_classsentinel "${SRC}/classsentinel.cpp") target_include_directories(test_classsentinel PRIVATE "${CMAKE_SOURCE_DIR}" @@ -47,3 +59,20 @@ target_link_libraries(test_classsentinel ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} ${LLVM_LIBRARIES}) add_test(NAME test_classsentinel COMMAND test_classsentinel) + +# +# test_reachingdefinitionspass +# + +add_executable(test_reachingdefinitionspass "${SRC}/ReachingDefinitionsPass.cpp") +target_include_directories(test_reachingdefinitionspass + PRIVATE "${CMAKE_SOURCE_DIR}" + "${Boost_INCLUDE_DIRS}") +target_compile_definitions(test_reachingdefinitionspass + PRIVATE "BOOST_TEST_DYN_LINK=1") +target_link_libraries(test_reachingdefinitionspass + Support + BasicAnalyses + ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} + ${LLVM_LIBRARIES}) +add_test(NAME test_reachingdefinitionspass COMMAND test_reachingdefinitionspass) diff --git a/tools/revamb/CMakeLists.txt b/tools/revamb/CMakeLists.txt index c9d3cae42..f61e85a45 100644 --- a/tools/revamb/CMakeLists.txt +++ b/tools/revamb/CMakeLists.txt @@ -14,7 +14,6 @@ add_executable(revamb NoReturnAnalysis.cpp OSRA.cpp PTCDump.cpp - ReachingDefinitionsPass.cpp SET.cpp SimplifyComparisonsPass.cpp VariableManager.cpp) diff --git a/tools/revamb/InstructionTranslator.h b/tools/revamb/InstructionTranslator.h index 88129c25b..909594988 100644 --- a/tools/revamb/InstructionTranslator.h +++ b/tools/revamb/InstructionTranslator.h @@ -11,6 +11,7 @@ #include // LLVM includes +#include "llvm/ADT/SmallSet.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Pass.h" #include "llvm/Support/ErrorOr.h" diff --git a/tools/revamb/JumpTargetManager.cpp b/tools/revamb/JumpTargetManager.cpp index 307d10a44..cb1bc4ca6 100644 --- a/tools/revamb/JumpTargetManager.cpp +++ b/tools/revamb/JumpTargetManager.cpp @@ -34,6 +34,7 @@ // Local libraries includes #include "revng/ADT/Queue.h" #include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" +#include "revng/BasicAnalyses/ReachingDefinitionsPass.h" #include "revng/Support/CommandLine.h" #include "revng/Support/Debug.h" #include "revng/Support/IRHelpers.h" diff --git a/tools/revamb/NoReturnAnalysis.h b/tools/revamb/NoReturnAnalysis.h index 27ca19e57..95b1efff2 100644 --- a/tools/revamb/NoReturnAnalysis.h +++ b/tools/revamb/NoReturnAnalysis.h @@ -16,11 +16,9 @@ #include "llvm/ADT/Triple.h" // Local libraries includes +#include "revng/BasicAnalyses/ReachingDefinitionsPass.h" #include "revng/Support/revng.h" -// Local includes -#include "ReachingDefinitionsPass.h" - namespace llvm { class BasicBlock; class CallInst; diff --git a/tools/revamb/OSRA.cpp b/tools/revamb/OSRA.cpp index 4dbb64365..37a56a21d 100644 --- a/tools/revamb/OSRA.cpp +++ b/tools/revamb/OSRA.cpp @@ -15,6 +15,7 @@ // LLVM includes #include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/Analysis/ConstantFolding.h" #include "llvm/IR/AssemblyAnnotationWriter.h" #include "llvm/IR/Constants.h" @@ -29,10 +30,10 @@ #include "revng/ADT/Queue.h" #include "revng/Support/Debug.h" #include "revng/Support/IRHelpers.h" +#include "revng/Support/MemoryAccess.h" #include "revng/Support/revng.h" // Local includes -#include "MemoryAccess.h" #include "OSRA.h" using namespace llvm; @@ -1973,7 +1974,7 @@ void OSRA::mergeLoadReacher(LoadInst *Load) { OSR ReachingOSR = P.second; if (ReachingOSR != Result) { OSR FreeOSR = createOSR(Load, Load->getParent()); - if (Reachers.size() == RDP.getReachingDefinitionsCount(Load)) { + if (Reachers.size() == RDP.getReachingDefinitions(Load).size()) { BoundedValue NewBVs = pathSensitiveMerge(Load); BVs.forceBV(Load, NewBVs); } diff --git a/tools/revamb/OSRA.h b/tools/revamb/OSRA.h index fb04a7333..0727f24e7 100644 --- a/tools/revamb/OSRA.h +++ b/tools/revamb/OSRA.h @@ -14,10 +14,10 @@ // Local libraries includes #include "revng/BasicAnalyses/FunctionCallIdentification.h" +#include "revng/BasicAnalyses/ReachingDefinitionsPass.h" #include "revng/Support/IRHelpers.h" // Local includes -#include "ReachingDefinitionsPass.h" #include "SimplifyComparisonsPass.h" // Forward declarations diff --git a/tools/revamb/ReachingDefinitionsPass.cpp b/tools/revamb/ReachingDefinitionsPass.cpp deleted file mode 100644 index 1e2044bda..000000000 --- a/tools/revamb/ReachingDefinitionsPass.cpp +++ /dev/null @@ -1,1183 +0,0 @@ -/// \file reachingdefinitions.cpp -/// \brief Implementation of the ReachingDefinitionsPass - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -// Standard includes -#include -#include -#include -#include -#include -#include -#include - -// LLVM includes -#include "llvm/ADT/PostOrderIterator.h" -#include "llvm/IR/Dominators.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/Support/Casting.h" - -// Local libraries includes -#include "revng/ADT/UniquedStack.h" -#include "revng/BasicAnalyses/FunctionCallIdentification.h" -#include "revng/Support/Debug.h" -#include "revng/Support/IRHelpers.h" - -// Local includes -#include "ReachingDefinitionsPass.h" - -using namespace llvm; - -using std::pair; -using std::queue; -using std::set; -using std::tie; -using std::unordered_map; -using std::vector; - -using IndexesVector = SmallVector; - -static Logger<> PropagationLog("rdp-propagation"); -static Logger<> CNPLog("cnp"); -static Logger<> RDPLog("rdp"); - -template -using vvector = const vector &; - -#define RDIP ReachingDefinitionsImplPass - -template class ReachingDefinitionsImplPass; -template class ReachingDefinitionsImplPass; - -template<> -char RDIP::ID = 0; - -template<> -char RDIP::ID = 0; - -template<> -char RDIP::ID = 0; - -template<> -char RDIP::ID = 0; - -using RegisterRDP = RegisterPass; -static RegisterRDP X1("rdp", "Reaching Definitions Pass", true, true); - -using RegisterRLP = RegisterPass; -static RegisterRLP X2("rlp", "Reaching Definitions Pass", true, true); - -// ReachingDefinitionsPass methods implementation - -template<> -const IndexesVector & -ReachingDefinitionsPass::getDefinedConditions(BasicBlock *BB) { - return ConditionNumberingPass::NoDefinedConditions; -} - -template<> -int32_t ReachingDefinitionsPass::getConditionIndex(TerminatorInst *V) { - return 0; -} - -template<> -void ReachingDefinitionsPass::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); -} - -// ReachedLoadsPass methods implementations - -template<> -const IndexesVector &ReachedLoadsPass::getDefinedConditions(BasicBlock *BB) { - return ConditionNumberingPass::NoDefinedConditions; -} - -template<> -int32_t ReachedLoadsPass::getConditionIndex(TerminatorInst *V) { - return 0; -} - -template<> -void ReachedLoadsPass::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); -} - -template class ReachingDefinitionsImplPass; -template class ReachingDefinitionsImplPass; - -using RegisterCRDP = RegisterPass; -const char *CRDPDescription = "Conditional Reaching Definitions Pass"; -static RegisterCRDP Y1("crdp", CRDPDescription, true, true); - -using RegisterCRLP = RegisterPass; -const char *CRLPDescription = "Conditional Reaching Definitions Pass"; -static RegisterCRLP Y2("crlp", CRLPDescription, true, true); - -// ConditionalReachingDefinitionsPass methods implementations - -template<> -const IndexesVector & -ConditionalReachingDefinitionsPass::getDefinedConditions(BasicBlock *BB) { - return getAnalysis().getDefinedConditions(BB); -} - -// TODO: this duplication sucks -template<> -int32_t -ConditionalReachingDefinitionsPass::getConditionIndex(TerminatorInst *T) { - auto *Branch = dyn_cast(T); - if (Branch == nullptr || !Branch->isConditional()) - return 0; - - return getAnalysis().getConditionIndex(T); -} - -using CRDP = ConditionalReachingDefinitionsPass; - -template<> -void CRDP::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); - AU.addRequired(); -} - -template<> -int32_t ConditionalReachedLoadsPass::getConditionIndex(TerminatorInst *T) { - auto *Branch = dyn_cast(T); - if (Branch == nullptr || !Branch->isConditional()) - return 0; - - return getAnalysis().getConditionIndex(T); -} - -// ConditionalReachedLoadsPass methods implementation - -template<> -const IndexesVector & -ConditionalReachedLoadsPass::getDefinedConditions(BasicBlock *BB) { - return getAnalysis().getDefinedConditions(BB); -} - -template<> -void ConditionalReachedLoadsPass::getAnalysisUsage(AnalysisUsage &AU) const { - AU.setPreservesAll(); - AU.addRequired(); - AU.addRequired(); -} - -static size_t combine(size_t A, size_t B) { - return (A << 1 | A >> 31) ^ B; -} - -static size_t combine(size_t A, void *Ptr) { - return combine(A, reinterpret_cast(Ptr)); -} - -static bool isSupportedOperator(unsigned Opcode) { - switch (Opcode) { - case Instruction::Xor: - case Instruction::And: - case Instruction::Or: - case Instruction::ICmp: - return true; - default: - return false; - } -} - -class ConditionHash { -public: - ConditionHash(ReachingDefinitionsPass &RDP) : RDP(RDP) {} - - size_t operator()(BranchInst *const &V) const; - -private: - ReachingDefinitionsPass &RDP; -}; - -size_t ConditionHash::operator()(BranchInst *const &B) const { - Value *V = B->getCondition(); - size_t Hash = 0; - queue WorkList; - WorkList.push(V); - while (!WorkList.empty()) { - Value *V; - V = WorkList.front(); - WorkList.pop(); - - bool IsStore = isa(V); - bool IsLoad = isa(V); - if (IsStore || IsLoad) { - // Load/store vs load/store - if (IsStore) { - Hash = combine(Hash, cast(V)->getPointerOperand()); - } else { - for (Instruction *I : RDP.getReachingDefinitions(cast(V))) { - if (auto *Store = dyn_cast(I)) - Hash = combine(Hash, Store->getPointerOperand()); - else if (auto *Load = dyn_cast(I)) - Hash = combine(Hash, Load->getPointerOperand()); - } - } - } else if (auto *I = dyn_cast(V)) { - // Instruction - if (!isSupportedOperator(I->getOpcode())) { - Hash = combine(Hash, V); - } else { - Hash = combine(Hash, I->getOpcode()); - Hash = combine(Hash, I->getNumOperands()); - for (unsigned C = 0; C < I->getNumOperands(); C++) - WorkList.push(I->getOperand(C)); - } - } else { - Hash = combine(Hash, V); - } - } - - return Hash; -} - -class ConditionEqualTo { -public: - ConditionEqualTo(ReachingDefinitionsPass &RDP) : RDP(RDP) {} - - bool operator()(BranchInst *const &A, BranchInst *const &B) const; - -private: - ReachingDefinitionsPass &RDP; -}; - -using BranchRef = BranchInst *const &; -bool ConditionEqualTo::operator()(BranchRef BA, BranchRef BB) const { - Value *A = BA->getCondition(); - Value *B = BB->getCondition(); - queue> WorkList; - WorkList.push({ A, B }); - while (!WorkList.empty()) { - Value *AV, *BV; - tie(AV, BV) = WorkList.front(); - WorkList.pop(); - - // Early continue in case they're exactly the same value - if (AV == BV) - continue; - - bool AIsStore = isa(AV); - bool AIsLoad = isa(AV); - bool BIsStore = isa(BV); - bool BIsLoad = isa(BV); - if ((AIsStore || AIsLoad) && (BIsStore || BIsLoad)) { - // Load/store vs load/store - vector AStores; - if (AIsStore) - AStores.push_back(cast(AV)); - else - AStores = RDP.getReachingDefinitions(cast(AV)); - - vector BStores; - if (BIsStore) - BStores.push_back(cast(BV)); - else - BStores = RDP.getReachingDefinitions(cast(BV)); - - if (AStores != BStores) - return false; - } else if (auto *AI = dyn_cast(AV)) { - // Instruction - auto *BI = dyn_cast(BV); - if (BI == nullptr || AI->getOpcode() != BI->getOpcode() - || AI->getNumOperands() != BI->getNumOperands() - || !isSupportedOperator(AI->getOpcode())) - return false; - - for (unsigned I = 0; I < AI->getNumOperands(); I++) - WorkList.push({ AI->getOperand(I), BI->getOperand(I) }); - } else { - return false; - } - } - return true; -} - -static SmallSet -resettingBasicBlocks(ReachingDefinitionsPass &RDP, BranchInst *const &Branch) { - SmallSet Result; - Value *A = Branch->getCondition(); - queue WorkList; - WorkList.push(A); - while (!WorkList.empty()) { - Value *AV; - AV = WorkList.front(); - WorkList.pop(); - - bool AIsStore = isa(AV); - bool AIsLoad = isa(AV); - if (AIsStore || AIsLoad) { - // Load/store vs load/store - vector AStores; - if (AIsStore) { - Result.insert(cast(AV)->getParent()); - } else { - for (Instruction *I : RDP.getReachingDefinitions(cast(AV))) { - Result.insert(I->getParent()); - } - } - - } else if (auto *AI = dyn_cast(AV)) { - // Instruction - if (!isSupportedOperator(AI->getOpcode())) - return {}; - - for (unsigned I = 0; I < AI->getNumOperands(); I++) - WorkList.push(AI->getOperand(I)); - } else if (!isa(AV)) { - return {}; - } - } - - return Result; -} - -char ConditionNumberingPass::ID = 0; -const IndexesVector ConditionNumberingPass::NoDefinedConditions; -using RegisterCNP = RegisterPass; -static RegisterCNP Z("cnp", "Condition Numbering Pass", true, true); - -template -static bool pushIfAbsent(C &Container, T Element) { - auto It = std::find(Container.begin(), Container.end(), Element); - bool Result = It != Container.end(); - if (!Result) - Container.push_back(Element); - return Result; -} - -/// \brief Support class for easily adding edges on the CFG using switch -/// instructions. -/// -/// FakeSwitch creates a SwitchInst to which the user can easily add cases, -/// without caring about the label value. Moreover, FakeSwitch automatically -/// backups and replaces the terminator instruction, if present, adds its -/// successors to the switch, and, when restore is called, restore it. -class FakeSwitch { -public: - FakeSwitch(BasicBlock *Target, unsigned NumCases) : - Target(Target), - SavedTerminator(nullptr), - Switch(nullptr), - Ty(IntegerType::get(getContext(Target), 32)), - NumCases(NumCases) { - - SavedTerminator = Target->getTerminator(); - if (SavedTerminator != nullptr) - this->NumCases += SavedTerminator->getNumSuccessors(); - } - - void add(BasicBlock *New) { - // Is this the first basic block being added? If so, create the switch and - // detach the old terminator instruction. - if (Switch == nullptr) { - // Create the switch statement and append it to the basic block - Switch = SwitchInst::Create(ConstantInt::get(Ty, 0), - New, - NumCases, - Target); - - // If there was a terminator save it and add all its successors to the - // switch - if (SavedTerminator != nullptr) { - SavedTerminator->removeFromParent(); - for (BasicBlock *Successor : SavedTerminator->successors()) { - // Note: this will never cause infinite recursion since we just - // initialized the Switch field - add(Successor); - } - } - } - - // Add the requested basic block - Switch->addCase(ConstantInt::get(Ty, Switch->getNumCases() + 1), New); - } - - void restore() { - // Check if we ever did anything - if (Switch == nullptr) - return; - - // We no longer need the switch - Switch->eraseFromParent(); - - // Restore the old terminator - if (SavedTerminator != nullptr) { - Target->getInstList().push_back(SavedTerminator); - revng_assert(Target->getTerminator() == SavedTerminator); - } - } - -private: - BasicBlock *Target; - TerminatorInst *SavedTerminator; - SwitchInst *Switch; - IntegerType *Ty; - unsigned NumCases; -}; - -bool ConditionNumberingPass::runOnFunction(Function &F) { - - revng_log(PassesLog, "Starting ConditionNumberingPass"); - - auto &Log = CNPLog; - - LLVMContext &C = F.getParent()->getContext(); - auto &RDP = getAnalysis(); - using cnp_hashmap = unordered_map, - ConditionHash, - ConditionEqualTo>; - cnp_hashmap Conditions(10, ConditionHash(RDP), ConditionEqualTo(RDP)); - - // Group conditions together - for (BasicBlock &BB : F) - if (auto *Branch = dyn_cast(BB.getTerminator())) - if (Branch->isConditional()) - Conditions[Branch].push_back(Branch); - - // Save the interesting results - uint32_t ConditionIndex = 0; - - // Initialize the vector of predecessors of BBs sharing the same condition - using BB = BasicBlock; - std::vector CommonPredecessors; - - // Debugging purposes only - std::map> ResettingBasicBlocks; - - for (auto &P : Conditions) { - // Ignore all the conditions present in a single branch - if (P.second.size() > 1) { - // 0 is a reserved value, since it doesn't have a corresponding negative - // value - ConditionIndex++; - - // Create the common predecessor and register it - auto *CommonPredecessor = BB::Create(C, "cp" + Twine(ConditionIndex), &F); - CommonPredecessors.push_back(CommonPredecessor); - - // Create the fake switch which will create the edges from the common - // predecessor to all the basic blocks containing the branches associated - // with this condition - FakeSwitch Switch(CommonPredecessor, P.second.size()); - - for (BranchInst *B : P.second) { - // Build the branch -> condition index mapping - BranchConditionNumberMap[B] = ConditionIndex; - - // Build the list of conditions defined by each basic block - for (BasicBlock *Definer : resettingBasicBlocks(RDP, B)) { - // Register that Definer defines ConditionIndex - pushIfAbsent(DefinedConditions[Definer], ConditionIndex); - - // Register that ConditionIndex is defined by Defined - if (Log.isEnabled()) - pushIfAbsent(ResettingBasicBlocks[ConditionIndex], Definer); - } - - // Add an edge from the common predecessor to this basic block - Switch.add(B->getParent()); - } - - if (Log.isEnabled()) { - Log << std::dec << ConditionIndex << ":"; - for (BranchInst *B : P.second) - Log << " " << getName(B); - - auto It = P.second.begin(); - if (It != P.second.end()) { - Log << " (defined by:"; - for (BasicBlock *Definer : resettingBasicBlocks(RDP, *It)) - Log << " " << getName(Definer); - Log << ")"; - } - Log << DoLog; - } - } - } - - // Make each common predecessor reachable from the entry point, so that the - // PDT can take them into account. - FakeSwitch EntrySwitch(&F.getEntryBlock(), CommonPredecessors.size()); - for (BasicBlock *CommonPredecessor : CommonPredecessors) - EntrySwitch.add(CommonPredecessor); - - // Compute the post-dominator tree - DominatorTreeBase PDT(true); - PDT.recalculate(F); - - // Get the immediate post-dominator of each temporary basic block and then - // delete it - for (unsigned I = 0; I < CommonPredecessors.size(); I++) { - BasicBlock *CommonPredecessor = CommonPredecessors[I]; - - if (Log.isEnabled()) { - Log << "Condition index " << (I + 1) << " ("; - for (BasicBlock *Successor : successors(CommonPredecessor)) - Log << getName(Successor) << " "; - Log << ")"; - - Log << ", defined by"; - for (BasicBlock *Defined : ResettingBasicBlocks[I + 1]) - Log << " " << getName(Defined); - }; - - // Get the immediate post-dominator of the common predecessor - auto *PDTNode = PDT.getNode(CommonPredecessor); - - // Check if it's reachable from the exit (i.e., it's not part of an infinite - // loop). - BasicBlock *ImmediatePostDominator = nullptr; - // TODO: for some reason getBlock() might give nullptr, investigate - if (PDTNode != nullptr) - ImmediatePostDominator = PDTNode->getIDom()->getBlock(); - - if (ImmediatePostDominator != nullptr) { - - // Add the current ConditionIndex to those defined by it - // Note: ConditionIndex 0 is reserved, so we add one - for (BasicBlock *Successor : successors(ImmediatePostDominator)) - pushIfAbsent(DefinedConditions[Successor], I + 1); - - if (Log.isEnabled()) - Log << ", post-dominated by " << getName(ImmediatePostDominator); - - } else { - Log << ", no post dominator"; - } - - Log << DoLog; - } - - // Restore the entry block's terminator instruction - EntrySwitch.restore(); - - // Delete all the common predecessor basic blocks, we no longer need them - for (BasicBlock *CommonPredecessor : CommonPredecessors) - CommonPredecessor->eraseFromParent(); - - revng_log(PassesLog, "Ending ConditionNumberingPass"); - return false; -} - -void BasicBlockInfo::dump(std::ostream &Output) { - set Printed; - for (const MemoryInstruction &MI : Reaching) { - Instruction *V = MI.I; - if (Printed.count(V) == 0) { - Printed.insert(V); - Output << " " << getName(V); - } - } -} - -void BasicBlockInfo::newDefinition(StoreInst *Store, TypeSizeProvider &TSP) { - // Remove all the aliased reaching definitions - MemoryAccess TargetMA(Store, TSP); - auto Match = [&TargetMA](MemoryInstruction &MI) { - return TargetMA.mayAlias(MI.MA); - }; - removeDefinitions(Match); - - // Add this definition - Definitions.push_back(MemoryInstruction(Store, TSP)); -} - -LoadDefinitionType -BasicBlockInfo::newDefinition(LoadInst *Load, TypeSizeProvider &TSP) { - LoadDefinitionType Result = NoReachingDefinitions; - - // Check if it's a self-referencing load - MemoryAccess TargetMA(Load, TSP); - for (auto &MI : Definitions) { - auto *Definition = MI.I; - if (Definition == Load) { - // It's self-referencing, suppress all the matching loads - removeDefinitions([&TargetMA](MemoryInstruction &MI) { - return isa(MI.I) && TargetMA == MI.MA; - }); - Result = SelfReaching; - break; - } else if (TargetMA == MI.MA) { - Result = HasReachingDefinitions; - } - } - - // Add this definition - if (Result == NoReachingDefinitions) - Definitions.push_back(MemoryInstruction(Load, TSP)); - - return Result; -} - -bool BasicBlockInfo::propagateTo(BasicBlockInfo &Target, - TypeSizeProvider &TSP, - const IndexesVector &, - int32_t NewConditionIndex) { - bool Changed = false; - for (MemoryInstruction &Definition : Definitions) - Changed |= Target.Reaching.insert(Definition).second; - - return Changed; -} - -vector> -BasicBlockInfo::getReachingDefinitions(set &WhiteList, - TypeSizeProvider &TSP) { - vector> Result; - for (const MemoryInstruction &MI : Reaching) { - Instruction *I = MI.I; - if (auto *Load = dyn_cast(I)) { - // If it's a load check it's whitelisted - if (WhiteList.count(Load) != 0) - Result.push_back({ Load, MI.MA }); - } else { - // It's a store - Result.push_back({ I, MI.MA }); - } - } - - freeContainer(Reaching); - revng_assert(Reaching.size() == 0); - - return Result; -} - -void ConditionalBasicBlockInfo::dump(std::ostream &Output) { - set Printed; - for (auto &P : Reaching) { - Instruction *I = P.first.I; - if (Printed.count(I) == 0) { - Printed.insert(I); - Output << " " << getName(I); - } - } -} - -void ConditionalBasicBlockInfo::newDefinition(StoreInst *Store, - TypeSizeProvider &TSP) { - // Remove all the aliased reaching definitions - MemoryAccess TargetMA(Store, TSP); - removeDefinitions([&TargetMA](CondDefPair &P) { - // TODO: don't erase if conditions are complementary - return TargetMA.mayAlias(P.second.MA); - }); - - // Perform the merge - // Note that the new definition absorbes all the conditions holding in the - // current basic block - mergeDefinition({ Conditions, MemoryInstruction(Store, TSP) }, - Definitions, - TSP); -} - -LoadDefinitionType -ConditionalBasicBlockInfo::newDefinition(LoadInst *Load, - TypeSizeProvider &TSP) { - LoadDefinitionType Result = NoReachingDefinitions; - - // Check if it's a self-referencing load - MemoryAccess TargetMA(Load, TSP); - for (auto &P : Definitions) { - auto *Definition = P.second.I; - if (Definition == Load) { - // It's self-referencing, suppress all the matching loads - removeDefinitions([&TargetMA](CondDefPair &P) { - // TODO: can we embed if it's a load or a store in - // MemoryInstruction? - return isa(P.second.I) && P.second.MA == TargetMA; - }); - Result = SelfReaching; - break; - } else if (TargetMA == P.second.MA) { - Result = HasReachingDefinitions; - } - } - - // Add this definition - if (Result == NoReachingDefinitions) - mergeDefinition({ Conditions, MemoryInstruction(Load, TSP) }, - Definitions, - TSP); - - return Result; -} - -vector> -ConditionalBasicBlockInfo::getReachingDefinitions(set &WhiteList, - TypeSizeProvider &TSP) { - vector> Result; - for (auto &P : Reaching) { - Instruction *I = P.first.I; - if (auto *Load = dyn_cast(I)) { - // If it's a load check it's whitelisted - if (WhiteList.count(Load) != 0) - Result.push_back({ Load, P.first.MA }); - } else { - // It's a store - Result.push_back({ I, P.first.MA }); - } - } - - freeContainer(Reaching); - - return Result; -} - -bool ConditionalBasicBlockInfo::setIndexIfSeen(BitVector &Target, - int32_t Index) const { - auto ConditionIt = std::find(SeenConditions.begin(), - SeenConditions.end(), - Index); - - // If present set the corresponding bit in Defined - if (ConditionIt != SeenConditions.end()) { - Target.set(ConditionIt - SeenConditions.begin()); - return true; - } - - return false; -} - -bool ConditionalBasicBlockInfo::propagateTo(ConditionalBasicBlockInfo &Target, - TypeSizeProvider &TSP, - const IndexesVector &DefinedIndexes, - int32_t NewConditionIndex) { - bool Changed = false; - - // Get (and insert, if necessary) the bit associated to the new - // condition. This bit will be set in all the definitions being propagated. - PropagationLog << " Adding conditions:"; - unsigned NewConditionBitIndex = Target.getConditionIndex(NewConditionIndex); - if (NewConditionIndex != 0 && !Target.Conditions[NewConditionBitIndex]) { - Target.Conditions.set(NewConditionBitIndex); - PropagationLog << " " << NewConditionIndex; - Changed = true; - } - - // Condition propagation - for (int SetBitIndex = Conditions.find_first(); SetBitIndex != -1; - SetBitIndex = Conditions.find_next(SetBitIndex)) { - int32_t ToPropagate = SeenConditions[SetBitIndex]; - - // Do not propagate the condition if: - // - // * it's defined in the target basic block - // * it's the condition associated to the current branch - // * the target basic block already has it - // - auto It = std::find_if(DefinedIndexes.begin(), - DefinedIndexes.end(), - [ToPropagate](int32_t Defined) { - return Defined == ToPropagate - || Defined == -ToPropagate; - }); - - if (ToPropagate != NewConditionIndex && ToPropagate != -NewConditionIndex - && It == DefinedIndexes.end() && !Target.hasCondition(ToPropagate)) { - Target.addCondition(ToPropagate); - PropagationLog << " " << ToPropagate; - Changed = true; - } - } - PropagationLog << DoLog; - - // Compute a bit vector with all the conditions that are incompatible with the - // target - BitVector Banned(SeenConditions.size()); - PropagationLog << " Banned conditions:"; - - // For each set bit in the target's conditions - for (int SetBitIndex = Target.Conditions.find_first(); SetBitIndex != -1; - SetBitIndex = Target.Conditions.find_next(SetBitIndex)) { - - // Consider the opposite condition as banned - int32_t BannedIndex = -Target.SeenConditions[SetBitIndex]; - PropagationLog << " " << BannedIndex; - - // Check BannedIndex is not explicitly allowed - auto It = std::find(Target.SeenConditions.begin(), - Target.SeenConditions.end(), - BannedIndex); - bool IsAllowed = It != Target.SeenConditions.end() - && Target.Conditions[It - Target.SeenConditions.begin()]; - if (!IsAllowed) - setIndexIfSeen(Banned, BannedIndex); - } - - PropagationLog << DoLog; - - // Create a BitVector for conditions defined in the target basic block, so - // that we can later exclude them - BitVector Defined(SeenConditions.size()); - for (int32_t DefinedIndex : DefinedIndexes) { - setIndexIfSeen(Defined, DefinedIndex); - setIndexIfSeen(Defined, -DefinedIndex); - } - BitVector NotDefined = Defined; - NotDefined.flip(); - - for (auto &Definition : Definitions) { - BitVector DefinitionConditions = Definition.first; - if (PropagationLog.isEnabled()) { - PropagationLog << " Propagate " << getName(Definition.second.I); - - if (auto *Load = dyn_cast(Definition.second.I)) - PropagationLog << " about " - << Load->getPointerOperand()->getName().str(); - else if (auto *Store = dyn_cast(Definition.second.I)) - PropagationLog << " about " - << Store->getPointerOperand()->getName().str(); - - if (DefinitionConditions.any()) { - PropagationLog << " (conditions:"; - for (int I = DefinitionConditions.find_first(); I != -1; - I = DefinitionConditions.find_next(I)) { - PropagationLog << " " << SeenConditions[I]; - } - PropagationLog << ")"; - } - - PropagationLog << "? "; - } - - // Reset all the conditions that are defined in the target basic block - DefinitionConditions &= NotDefined; - - // Check if this definition is compatible with the target basic block - auto BannedConditions = DefinitionConditions; - BannedConditions &= Banned; - if (BannedConditions.any()) { - PropagationLog << "no" << DoLog; - continue; - } - - PropagationLog << "yes" << DoLog; - - // Translate the conditions bitvector to the context of the target BBI - BitVector Translated(Target.SeenConditions.size()); - - for (int I = DefinitionConditions.find_first(); I != -1; - I = DefinitionConditions.find_next(I)) { - // Make sure the target BBI knows about all the necessary conditions - revng_assert(I < static_cast(SeenConditions.size())); - unsigned Index = Target.getConditionIndex(SeenConditions[I]); - unsigned OppositeIndex = Target.getConditionIndex(-SeenConditions[I]); - - // Keep the size of the new bitvector in sync - if (Target.SeenConditions.size() != Translated.size()) - Translated.resize(Target.SeenConditions.size()); - - Translated.set(Index); - Translated.reset(OppositeIndex); - } - - // Add the condition of this branch - if (NewConditionIndex != 0) - Translated.set(NewConditionBitIndex); - - Changed |= Target.mergeDefinition({ Translated, Definition.second }, - Target.Reaching, - TSP); - - revng_log(PropagationLog, " Changed? " << Changed); - } - - return Changed; -} - -bool ConditionalBasicBlockInfo::mergeDefinition(CondDefPair NewDefinition, - vector &Targets, - TypeSizeProvider &TSP) const { - BitVector &NewConditionsBV = NewDefinition.first; - revng_assert(NewConditionsBV.size() == SeenConditions.size()); - - for (CondDefPair &Target : Targets) { - // Does this definition matches the one we're looking for? - if (Target.second.I == NewDefinition.second.I) { - - // Are we saying something new? If so, merge the conditions. - if (Target.first != NewConditionsBV) { - Target.first |= NewConditionsBV; - return true; - } else { - return false; - } - } - } - - // This definition is new, register it - Targets.push_back(NewDefinition); - - return true; -} - -bool ConditionalBasicBlockInfo::mergeDefinition(CondDefPair NewDefinition, - ReachingType &Targets, - TypeSizeProvider &TSP) const { - BitVector &NewConditionsBV = NewDefinition.first; - revng_assert(NewConditionsBV.size() == SeenConditions.size()); - - // Merge the conditions of the new definition - BitVector &BV = Targets[NewDefinition.second]; - BitVector Old = BV; - BV |= NewConditionsBV; - - // Check if the new conditions are different from the initial ones - return Old != BV; -} - -template -bool ReachingDefinitionsImplPass::runOnFunction(Function &F) { - auto &Log = RDPLog; - - auto &FCI = getAnalysis(); - - if (std::is_same::value) - revng_log(PassesLog, "Starting ConditionalReachingDefinitionsPass"); - else - revng_log(PassesLog, "Starting ReachingDefinitionsPass"); - - for (auto &BB : F) { - if (!BB.empty()) { - if (auto *Call = dyn_cast(&*BB.begin())) { - Function *Callee = Call->getCalledFunction(); - // TODO: comparing with "newpc" string is sad - if (Callee != nullptr && Callee->getName() == "newpc") - break; - } - } - BasicBlockBlackList.insert(&BB); - } - - TypeSizeProvider TSP(F.getParent()->getDataLayout()); - - // Initialize queue - unsigned BasicBlockCount = 0; - unsigned BasicBlockVisits = 0; - ReversePostOrderTraversal RPOT(&F); - UniquedStack ToVisit; - for (BasicBlock *BB : RPOT) { - ToVisit.insert(BB); - BasicBlockCount++; - } - ToVisit.reverse(); - - while (!ToVisit.empty()) { - BasicBlockVisits++; - BasicBlock *BB = ToVisit.pop(); - - BBI &Info = DefinitionsMap[BB]; - Info.resetDefinitions(TSP); - - // Find all the definitions - for (Instruction &I : *BB) { - auto *Store = dyn_cast(&I); - auto *Load = dyn_cast(&I); - - if (Store != nullptr && MemoryAccess(Store, TSP).isValid()) { - - // Record new definition - Info.newDefinition(Store, TSP); - - } else if (Load != nullptr && MemoryAccess(Load, TSP).isValid()) { - - // Check if it's a new definition and record it - auto LoadType = Info.newDefinition(Load, TSP); - switch (LoadType) { - case NoReachingDefinitions: - NRDLoads.insert(Load); - break; - case SelfReaching: - SelfReachingLoads.insert(Load); - break; - case HasReachingDefinitions: - NRDLoads.erase(Load); - break; - } - } - } - - // TODO: this is an hack and should be replaced once we integrate calling - // convention and call graph in the basic block harvesting process - unsigned SuccessorsCount = succ_end(BB) - succ_begin(BB); - unsigned Size = Info.size(); - if (!FCI.isCall(BB) && Size * SuccessorsCount <= 5000) { - // Get the identifier of the conditional instruction - int32_t ConditionIndex = getConditionIndex(BB->getTerminator()); - revng_assert(ConditionIndex == 0 || ConditionIndex > 0); - - // Propagate definitions to successors, checking if actually we changed - // something, and if so re-enqueue them - for (BasicBlock *Successor : successors(BB)) { - if (BasicBlockBlackList.count(Successor) != 0) - continue; - - const IndexesVector &Conditions = getDefinedConditions(Successor); - - BBI &SuccessorInfo = DefinitionsMap[Successor]; - - if (PropagationLog.isEnabled()) { - PropagationLog << "Propagating from " << getName(BB) << " to " - << getName(Successor); - - if (Conditions.size() > 0) { - PropagationLog << " (resetting conditions: "; - for (int32_t ConditionIndex : Conditions) - PropagationLog << " " << ConditionIndex; - PropagationLog << ")"; - } - - if (ConditionIndex != 0) - PropagationLog << ", using a " << ConditionIndex << " branch" - << " (" << getName(BB->getTerminator()) << ")"; - - PropagationLog << DoLog; - } - - // Enqueue the successor only if the propagation actually did something - unsigned Old = SuccessorInfo.size(); - if (Info.propagateTo(SuccessorInfo, TSP, Conditions, ConditionIndex)) - ToVisit.insert(Successor); - - revng_log(PropagationLog, - getName(Successor) - << std::dec << " got " << (SuccessorInfo.size() - Old) - << " new reachers " - << "from " << getName(BB) << " (had " << Old << ")"); - - // Add the condition relative to the current branch instruction (if any) - if (ConditionIndex != 0) { - // If ConditionIndex is positive we're in the true branch, prepare - // ConditionIndex for the false branch - if (ConditionIndex > 0) - ConditionIndex = -ConditionIndex; - } - } - - // We no longer need to keep track of the definitions - Info.clearDefinitions(); - } - } - - // Collect final information - std::set &FreeLoads = NRDLoads; - FreeLoads.insert(SelfReachingLoads.begin(), SelfReachingLoads.end()); - - for (auto &P : DefinitionsMap) { - BasicBlock *BB = P.first; - BBI &Info = P.second; - - // TODO: use a list? - vector> Definitions; - Definitions = Info.getReachingDefinitions(FreeLoads, TSP); - for (Instruction &I : *BB) { - auto *Store = dyn_cast(&I); - auto *Load = dyn_cast(&I); - - using IMP = pair; - if (Store != nullptr) { - // Remove all the reaching definitions aliased by this store - MemoryAccess TargetMA(Store, TSP); - if (!TargetMA.isValid()) - continue; - - erase_if(Definitions, - [&TargetMA](IMP &P) { return TargetMA.mayAlias(P.second); }); - Definitions.push_back({ Store, TargetMA }); - - } else if (Load != nullptr) { - - // Record all the relevant reaching defininitions - MemoryAccess TargetMA(Load, TSP); - if (!TargetMA.isValid()) - continue; - - if (FreeLoads.count(Load) != 0) { - - // If it's a free load, remove all the matching loads - erase_if(Definitions, [&TargetMA, &TSP](IMP &P) { - Instruction *I = P.first; - return isa(I) && MemoryAccess(I, TSP) == TargetMA; - }); - Definitions.push_back({ Load, TargetMA }); - - } else { - - if (R == ReachingDefinitionsResult::ReachedLoads) { - for (auto &Definition : Definitions) { - if (TargetMA == Definition.second) { - ReachedLoads[Definition.first].push_back(Load); - ReachingDefinitionsCount[Load]++; - } - } - } - - std::vector LoadDefinitions; - for (auto &Definition : Definitions) - if (TargetMA == Definition.second) - LoadDefinitions.push_back(Definition.first); - - // Save them in ReachingDefinitions - std::sort(LoadDefinitions.begin(), LoadDefinitions.end()); - if (Log.isEnabled()) { - Log << getName(Load) << " is reached by:"; - for (auto *Definition : LoadDefinitions) - Log << " " << getName(Definition); - Log << DoLog; - } - ReachingDefinitions[Load] = std::move(LoadDefinitions); - } - } - } - } - - revng_log(Log, - "Basic blocks: " - << std::dec << BasicBlockCount << "\n" - << "Visited: " << std::dec << BasicBlockVisits << "\n" - << "Average visits per basic block: " << std::setprecision(2) - << float(BasicBlockVisits) / BasicBlockCount); - - if (R == ReachingDefinitionsResult::ReachedLoads) { - if (Log.isEnabled()) { - for (auto P : ReachedLoads) { - Log << getName(P.first) << " reaches"; - for (auto *Load : P.second) - Log << " " << getName(Load); - Log << DoLog; - } - } - } - - // Clear all the temporary data that is not part of the analysis result - freeContainer(DefinitionsMap); - freeContainer(FreeLoads); - freeContainer(BasicBlockBlackList); - freeContainer(NRDLoads); - freeContainer(SelfReachingLoads); - - if (std::is_same::value) - revng_log(PassesLog, "Ending ConditionalReachingDefinitionsPass"); - else - revng_log(PassesLog, "Ending ReachingDefinitionsPass"); - - return false; -} diff --git a/tools/revamb/ReachingDefinitionsPass.h b/tools/revamb/ReachingDefinitionsPass.h deleted file mode 100644 index 22889ad56..000000000 --- a/tools/revamb/ReachingDefinitionsPass.h +++ /dev/null @@ -1,391 +0,0 @@ -#ifndef REACHINGDEFINITIONS_H -#define REACHINGDEFINITIONS_H - -// -// This file is distributed under the MIT License. See LICENSE.md for details. -// - -// Standard includes -#include -#include - -// LLVM includes -#include "llvm/ADT/SmallBitVector.h" -#include "llvm/ADT/SmallSet.h" -#include "llvm/Pass.h" - -// Local includes -#include "MemoryAccess.h" -#include "revng/Support/Debug.h" -#include "revng/Support/IRHelpers.h" - -#define BitVector SmallBitVector - -namespace llvm { -class Instruction; -class StoreInst; -class LoadInst; -class Value; -class BranchInst; -class TerminatorInst; -}; // namespace llvm - -// TODO: [speedup] Use LoadStorePtr -// TODO: store in definitions/reaching the MemoryAccess - -enum class ReachingDefinitionsResult { ReachingDefinitions, ReachedLoads }; - -template -class ReachingDefinitionsImplPass; - -enum LoadDefinitionType { - NoReachingDefinitions, ///< No one can reach it - SelfReaching, ///< Can see it self - HasReachingDefinitions -}; - -struct MemoryInstruction { - MemoryInstruction(llvm::Instruction *I, TypeSizeProvider &TSP) : - I(I), - MA(I, TSP) {} - MemoryInstruction(llvm::StoreInst *I, TypeSizeProvider &TSP) : - I(I), - MA(I, TSP) {} - MemoryInstruction(llvm::LoadInst *I, TypeSizeProvider &TSP) : - I(I), - MA(I, TSP) {} - - bool operator<(const MemoryInstruction Other) const { return I < Other.I; } - - bool operator==(const MemoryInstruction Other) const { return I == Other.I; } - - llvm::Instruction *I; - MemoryAccess MA; -}; - -namespace std { -template<> -struct hash { - size_t operator()(const MemoryInstruction &MI) const { - return std::hash()(MI.I); - } -}; -} // namespace std - -class BasicBlockInfo { -public: - unsigned addCondition(int32_t ConditionIndex) { revng_abort(); } - - void resetDefinitions(TypeSizeProvider &TSP) { - Definitions.clear(); - // for (llvm::Instruction *I : Reaching) - // Definitions.push_back(MemoryInstruction(I, TSP)); - std::copy(Reaching.begin(), - Reaching.end(), - std::back_inserter(Definitions)); - } - - unsigned size() const { return Reaching.size(); } - - void clearDefinitions() { Definitions.clear(); } - - void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP); - LoadDefinitionType newDefinition(llvm::LoadInst *Load, TypeSizeProvider &TSP); - bool propagateTo(BasicBlockInfo &Target, - TypeSizeProvider &TSP, - const llvm::SmallVector &DefinedIndexes, - int32_t NewConditionIndex); - - std::vector> - getReachingDefinitions(std::set &WhiteList, - TypeSizeProvider &TSP); - - void dump(std::ostream &Output); - -private: - template - void removeDefinitions(UnaryPredicate P) { - erase_if(Definitions, P); - } - -private: - // llvm::SmallSet Reaching; - std::unordered_set Reaching; - std::vector Definitions; -}; - -class ConditionalBasicBlockInfo { -public: - unsigned addCondition(int32_t ConditionIndex) { - unsigned Result = getConditionIndex(ConditionIndex); - Conditions.set(Result); - return Result; - } - - bool hasCondition(int32_t ConditionIndex) { - unsigned Result = getConditionIndex(ConditionIndex); - return Conditions[Result]; - } - - void resetDefinitions(TypeSizeProvider &TSP) { - for (auto &P : Reaching) - Definitions.push_back({ P.second, P.first }); - } - - unsigned size() const { return Reaching.size(); } - - void clearDefinitions() { Definitions.clear(); } - - void newDefinition(llvm::StoreInst *Store, TypeSizeProvider &TSP); - LoadDefinitionType newDefinition(llvm::LoadInst *Load, TypeSizeProvider &TSP); - bool propagateTo(ConditionalBasicBlockInfo &Target, - TypeSizeProvider &TSP, - const llvm::SmallVector &DefinedIndexes, - int32_t NewConditionIndex); - - std::vector> - getReachingDefinitions(std::set &WhiteList, - TypeSizeProvider &TSP); - - void dump(std::ostream &Output); - -private: - using CondDefPair = std::pair; - using ReachingType = std::unordered_map; - - enum ConditionsComparison { Identical, Different, Complementary }; - -private: - /// \brief Set the bit corresponding to \p Index in \p Target, if present in - /// SeenCondtions. - bool setIndexIfSeen(llvm::BitVector &Target, int32_t Index) const; - - template - void removeDefinitions(UnaryPredicate P) { - erase_if(Definitions, P); - } - - unsigned getConditionIndex(uint32_t ConditionIndex) { - auto It = std::find(SeenConditions.begin(), - SeenConditions.end(), - ConditionIndex); - - if (It != SeenConditions.end()) { - return It - SeenConditions.begin(); - } else { - SeenConditions.push_back(ConditionIndex); - auto NewSize = SeenConditions.size(); - - Conditions.resize(NewSize); - for (auto &P : Reaching) - P.second.resize(NewSize); - for (CondDefPair &Definition : Definitions) - Definition.first.resize(NewSize); - - return NewSize - 1; - } - } - - bool mergeDefinition(CondDefPair NewDefinition, - std::vector &Targets, - TypeSizeProvider &TSP) const; - - bool mergeDefinition(CondDefPair NewDefinition, - ReachingType &Targets, - TypeSizeProvider &TSP) const; - -private: - // Seen conditions - std::vector SeenConditions; - // TODO: switch to list? - ReachingType Reaching; - std::vector Definitions; - llvm::BitVector Conditions; -}; - -template -using RDIP = ReachingDefinitionsImplPass; - -using RDR = ReachingDefinitionsResult; -using CBBI = ConditionalBasicBlockInfo; - -using ReachingDefinitionsPass = RDIP; -using ConditionalReachingDefinitionsPass = RDIP; - -using ReachedLoadsPass = RDIP; -using ConditionalReachedLoadsPass = RDIP; - -template -class ReachingDefinitionsImplPass : public llvm::FunctionPass { -public: - static char ID; - - ReachingDefinitionsImplPass() : llvm::FunctionPass(ID){}; - - bool runOnFunction(llvm::Function &F) override; - - void getAnalysisUsage(llvm::AnalysisUsage &AU) const override; - - const std::vector & - getReachedLoads(const llvm::Instruction *I) { - revng_assert(R == ReachingDefinitionsResult::ReachedLoads); - return ReachedLoads[I]; - } - - const std::vector & - getReachingDefinitions(const llvm::LoadInst *Load) { - return ReachingDefinitions[Load]; - } - - unsigned getReachingDefinitionsCount(const llvm::LoadInst *Load) { - revng_assert(R == ReachingDefinitionsResult::ReachedLoads); - return ReachingDefinitionsCount[Load]; - } - - virtual void releaseMemory() override { - revng_log(ReleaseLog, "ReachingDefinitionsImplPass is releasing memory"); - freeContainer(ReachedLoads); - freeContainer(ReachingDefinitions); - freeContainer(ReachingDefinitionsCount); - } - -private: - int32_t getConditionIndex(llvm::TerminatorInst *T); - const llvm::SmallVector & - getDefinedConditions(llvm::BasicBlock *BB); - -private: - using BasicBlock = llvm::BasicBlock; - using LoadInst = llvm::LoadInst; - using Instruction = llvm::Instruction; - std::map DefinitionsMap; - std::set BasicBlockBlackList; - std::set NRDLoads; - std::set SelfReachingLoads; - std::map> ReachedLoads; - std::map> ReachingDefinitions; - std::map ReachingDefinitionsCount; -}; - -template<> -char RDIP::ID; - -template<> -char RDIP::ID; - -template<> -char RDIP::ID; - -template<> -char RDIP::ID; - -/// The ConditionNumberingPass loops over all the conditional branch -/// instructions in the program and tries to identify those that are based on -/// exactly the same condition, i.e., the pair for which can be sure that, if -/// the first branch is taken, then also the second branch will be taken. This -/// is particularly useful to handle consecutive predeicate instructions. -/// -/// Two conditions are considered the same, if they actually are the same or if -/// they compute exactly the same operations on the same operands. To -/// efficiently identify which branch instructions use the same conditions we -/// populate an hashmap with a custom hash function. At the end, we will discard -/// all the entries of the hashmap with a single entry, since we're not -/// interested in considering a condition if it doesn't have at least a -/// companion branch instruction. Each condition with at least two branches -/// using it is assigned a unique identifier, the condition index. -/// -/// The ConditionNumberingPass also provides, for each condition index, a list -/// of "reset" basic blocks, i.e., a list of basic blocks which define at least -/// one of the values involved in the computation of the condition. Such a list -/// can be used to understand when it doesn't make sense for an analysis to -/// consider that a certain condition is still holding. -/// -/// "reset" basic blocks also include the last basic block that might be -/// affected by the associated condition index. This is useful to prevent an -/// analysis from keeping track of a condition index which we can be sure will -/// never be used again. The last basic block that might be affected by a -/// condition index is the immediate post-dominator of the set of basic blocks -/// containing the branches associated to that condition index. -/// -/// The following figures examplifies the situation: BB1 and BB2 share the same -/// condition, BB3 is their immediate post-dominator. To easily identify it as -/// such we introduce a temporary basic block BB0 and make it a predecessor of -/// both BB1 and BB2. Then, we compute the post-dominator tree and ask for the -/// immediate post-domiantor of BB0, obtaining BB3. -/// -/// +-----------+ -/// | | -/// +- - - - - -+ BB0 +- - - - -+ -/// | | | | -/// +-----------+ -/// | | -/// -/// +-----v-----+ +-----v-----+ -/// | | | | -/// +---+ BB1 +---+ +---+ BB2 +---+ -/// | | | | | | | | -/// | +-----------+ | | +-----------+ | -/// | | | | -/// | | | | -/// +-----v-----+ +-----v-----+ +-----v-----+ +-----v-----+ -/// | | | | | | | | -/// | | | | | | | | -/// | | | | | | | | -/// +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ -/// | | | | -/// | | | | -/// | +-----v-----+ | +-----v-----+ -/// | | | | | | -/// +-------------> <-------+ | | -/// | | | | -/// +-----+-----+ +-----+-----+ -/// | | -/// | | -/// | +-----------+ | -/// | | | | -/// +----------> BB3 <----------+ -/// | | -/// +-----+-----+ -/// | -/// | -/// v -class ConditionNumberingPass : public llvm::FunctionPass { -public: - static char ID; - - static const llvm::SmallVector NoDefinedConditions; - - ConditionNumberingPass() : llvm::FunctionPass(ID){}; - - bool runOnFunction(llvm::Function &F) override; - - void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { - AU.addRequired(); - AU.setPreservesAll(); - } - - int32_t getConditionIndex(llvm::TerminatorInst *T) { - return BranchConditionNumberMap[T]; - } - - const llvm::SmallVector & - getDefinedConditions(llvm::BasicBlock *BB) const { - auto It = DefinedConditions.find(BB); - if (It == DefinedConditions.end()) - return NoDefinedConditions; - else - return It->second; - } - - virtual void releaseMemory() override { - revng_log(ReleaseLog, "ConditionNumberingPass is releasing memory"); - freeContainer(DefinedConditions); - freeContainer(BranchConditionNumberMap); - } - -private: - std::map> DefinedConditions; - std::map BranchConditionNumberMap; -}; - -#endif // REACHINGDEFINITIONS_H diff --git a/tools/revamb/SimplifyComparisonsPass.cpp b/tools/revamb/SimplifyComparisonsPass.cpp index e318e76af..ee533aa41 100644 --- a/tools/revamb/SimplifyComparisonsPass.cpp +++ b/tools/revamb/SimplifyComparisonsPass.cpp @@ -10,6 +10,7 @@ #include // LLVM includes +#include "llvm/ADT/SmallSet.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" diff --git a/tools/revamb/SimplifyComparisonsPass.h b/tools/revamb/SimplifyComparisonsPass.h index 3365027a2..89a4cd219 100644 --- a/tools/revamb/SimplifyComparisonsPass.h +++ b/tools/revamb/SimplifyComparisonsPass.h @@ -11,8 +11,8 @@ // LLVM includes #include "llvm/Pass.h" -// Local includes -#include "ReachingDefinitionsPass.h" +// Local libraries includes +#include "revng/BasicAnalyses/ReachingDefinitionsPass.h" /// \brief Look for sophisticated comparisons that can be simplified /// This pass looks for comparisons checkin for the sign of a value, and, if