From 223159957b9a50a9b0d5cd01c3e0befc3ea25549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Gia=20Phong?= Date: Tue, 5 Sep 2023 11:23:45 +0900 Subject: [PATCH] Remove vendored dg headers --- .gitignore | 1 - include/dg/ADT/Bits.h | 236 --- include/dg/ADT/Bitvector.h | 259 --- include/dg/ADT/DGContainer.h | 113 -- include/dg/ADT/DisjunctiveIntervalMap.h | 648 ------ include/dg/ADT/HashMap.h | 18 - include/dg/ADT/HashMapImpl.h | 35 - include/dg/ADT/IntervalsList.h | 132 -- include/dg/ADT/Map.h | 44 - include/dg/ADT/NumberSet.h | 283 --- include/dg/ADT/Queue.h | 138 -- include/dg/ADT/STLHashMap.h | 73 - include/dg/ADT/SetQueue.h | 38 - include/dg/ADT/TslHopscotchHashMap.h | 16 - include/dg/AnalysisOptions.h | 62 - include/dg/BBlock.h | 506 ----- include/dg/BBlockBase.h | 109 - include/dg/BBlocksBuilder.h | 96 - include/dg/BFS.h | 24 - include/dg/CallGraph/CallGraph.h | 113 -- include/dg/ControlDependence/CDGraph.h | 174 -- include/dg/ControlDependence/ControlClosure.h | 168 -- .../dg/ControlDependence/ControlDependence.h | 0 .../ControlDependenceAnalysisOptions.h | 50 - include/dg/ControlDependence/DOD.h | 625 ------ include/dg/ControlDependence/DODNTSCD.h | 71 - include/dg/ControlDependence/NTSCD.h | 305 --- include/dg/DFS.h | 24 - include/dg/DG2Dot.h | 639 ------ include/dg/DGParameters.h | 248 --- include/dg/DataDependence/DataDependence.h | 72 - .../DataDependenceAnalysisImpl.h | 60 - .../DataDependenceAnalysisOptions.h | 146 -- include/dg/DependenceGraph.h | 466 ----- include/dg/Dominators/DominanceFrontiers.h | 75 - .../dg/Dominators/PostDominanceFrontiers.h | 97 - include/dg/MemorySSA/Definitions.h | 94 - include/dg/MemorySSA/DefinitionsMap.h | 209 -- include/dg/MemorySSA/MemorySSA.h | 265 --- include/dg/MemorySSA/ModRef.h | 99 - include/dg/MemoryState.h | 113 -- include/dg/Node.h | 425 ---- include/dg/NodesWalk.h | 141 -- include/dg/Offset.h | 165 -- include/dg/PointerAnalysis/MemoryObject.h | 111 - include/dg/PointerAnalysis/PSNode.h | 765 ------- include/dg/PointerAnalysis/Pointer.h | 71 - include/dg/PointerAnalysis/PointerAnalysis.h | 200 -- .../dg/PointerAnalysis/PointerAnalysisFI.h | 89 - .../dg/PointerAnalysis/PointerAnalysisFS.h | 260 --- .../dg/PointerAnalysis/PointerAnalysisFSInv.h | 415 ---- .../PointerAnalysis/PointerAnalysisOptions.h | 32 - include/dg/PointerAnalysis/PointerGraph.h | 353 ---- .../PointerGraphOptimizations.h | 131 -- .../PointerAnalysis/PointerGraphValidator.h | 51 - include/dg/PointerAnalysis/PointsToMapping.h | 79 - include/dg/PointerAnalysis/PointsToSet.h | 21 - .../AlignedPointerIdPointsToSet.h | 258 --- .../AlignedSmallOffsetsPointsToSet.h | 265 --- .../PointsToSets/LookupTable.h | 127 -- .../PointsToSets/OffsetsSetPointsToSet.h | 229 --- .../PointsToSets/PointerIdPointsToSet.h | 219 -- .../PointsToSets/SeparateOffsetsPointsToSet.h | 193 -- .../PointsToSets/SimplePointsToSet.h | 156 -- .../PointsToSets/SmallOffsetsPointsToSet.h | 257 --- include/dg/ReadWriteGraph/DefSite.h | 208 -- include/dg/ReadWriteGraph/RWBBlock.h | 139 -- include/dg/ReadWriteGraph/RWNode.h | 468 ----- include/dg/ReadWriteGraph/RWSubgraph.h | 104 - include/dg/ReadWriteGraph/ReadWriteGraph.h | 128 -- include/dg/SCC.h | 170 -- include/dg/Slicing.h | 327 --- include/dg/SubgraphBase.h | 83 - include/dg/SubgraphNode.h | 427 ---- .../dg/SystemDependenceGraph/DGArgumentPair.h | 41 - include/dg/SystemDependenceGraph/DGBBlock.h | 50 - include/dg/SystemDependenceGraph/DGElement.h | 97 - include/dg/SystemDependenceGraph/DGNode.h | 86 - include/dg/SystemDependenceGraph/DGNodeCall.h | 41 - .../dg/SystemDependenceGraph/DGParameters.h | 116 -- .../dg/SystemDependenceGraph/DepDGElement.h | 148 -- .../SystemDependenceGraph/DependenceGraph.h | 170 -- .../SystemDependenceGraph.h | 60 - include/dg/legacy/Analysis.h | 86 - include/dg/legacy/BFS.h | 93 - include/dg/legacy/DFS.h | 158 -- include/dg/legacy/DataFlowAnalysis.h | 168 -- include/dg/legacy/NodesWalk.h | 389 ---- include/dg/llvm/CallGraph/CallGraph.h | 231 --- .../llvm/ControlDependence/ControlClosure.h | 141 -- .../ControlDependence/ControlDependence.h | 122 -- include/dg/llvm/ControlDependence/DOD.h | 351 ---- .../dg/llvm/ControlDependence/GraphBuilder.h | 147 -- .../dg/llvm/ControlDependence/IGraphBuilder.h | 296 --- .../ControlDependence/InterproceduralCD.h | 143 -- .../LLVMControlDependenceAnalysisImpl.h | 68 - .../LLVMControlDependenceAnalysisOptions.h | 22 - include/dg/llvm/ControlDependence/NTSCD.h | 349 ---- include/dg/llvm/ControlDependence/SCD.h | 89 - .../dg/llvm/ControlDependence/legacy/Block.h | 97 - .../llvm/ControlDependence/legacy/Function.h | 42 - .../ControlDependence/legacy/GraphBuilder.h | 79 - .../dg/llvm/ControlDependence/legacy/NTSCD.h | 132 -- .../ControlDependence/legacy/TarjanAnalysis.h | 175 -- .../dg/llvm/DataDependence/DataDependence.h | 118 -- .../LLVMDataDependenceAnalysisOptions.h | 55 - include/dg/llvm/DefUse/DefUse.h | 56 - include/dg/llvm/Dominators/Dominators.h | 95 - include/dg/llvm/ForkJoin/ForkJoin.h | 33 - include/dg/llvm/GraphBuilder.h | 286 --- include/dg/llvm/LLVMAnalysisOptions.h | 20 - include/dg/llvm/LLVMDG2Dot.h | 333 --- .../dg/llvm/LLVMDGAssemblyAnnotationWriter.h | 363 ---- include/dg/llvm/LLVMDGVerifier.h | 32 - include/dg/llvm/LLVMDependenceGraph.h | 207 -- include/dg/llvm/LLVMDependenceGraphBuilder.h | 251 --- include/dg/llvm/LLVMNode.h | 89 - include/dg/llvm/LLVMSlicer.h | 627 ------ .../llvm/PointerAnalysis/DGPointerAnalysis.h | 245 --- .../LLVMPointerAnalysisOptions.h | 23 - .../dg/llvm/PointerAnalysis/LLVMPointsToSet.h | 479 ----- .../dg/llvm/PointerAnalysis/PointerAnalysis.h | 348 ---- .../dg/llvm/PointerAnalysis/PointerGraph.h | 453 ----- .../PointerAnalysis/PointerGraphValidator.h | 29 - .../llvm/PointerAnalysis/SVFPointerAnalysis.h | 210 -- .../LLVMReadWriteGraphBuilder.h | 139 -- .../SystemDependenceGraph/AnnotationWriter.h | 0 .../dg/llvm/SystemDependenceGraph/SDG2Dot.h | 279 --- .../SystemDependenceGraph.h | 107 - .../SystemDependenceGraphBuilder.h | 226 --- .../dg/llvm/ThreadRegions/ControlFlowGraph.h | 56 - .../dg/llvm/ThreadRegions/Graphs/BlockGraph.h | 35 - .../Graphs/CriticalSectionsBuilder.h | 76 - .../llvm/ThreadRegions/Graphs/FunctionGraph.h | 32 - .../llvm/ThreadRegions/Graphs/GraphBuilder.h | 145 -- .../Graphs/ThreadRegionsBuilder.h | 62 - .../llvm/ThreadRegions/MayHappenInParallel.h | 18 - .../ThreadRegions/Nodes/CallFuncPtrNode.h | 12 - .../dg/llvm/ThreadRegions/Nodes/CallNode.h | 12 - .../llvm/ThreadRegions/Nodes/CallReturnNode.h | 12 - .../dg/llvm/ThreadRegions/Nodes/EntryNode.h | 29 - .../dg/llvm/ThreadRegions/Nodes/ExitNode.h | 31 - .../dg/llvm/ThreadRegions/Nodes/ForkNode.h | 36 - .../dg/llvm/ThreadRegions/Nodes/GeneralNode.h | 12 - .../dg/llvm/ThreadRegions/Nodes/JoinNode.h | 37 - .../dg/llvm/ThreadRegions/Nodes/LockNode.h | 20 - include/dg/llvm/ThreadRegions/Nodes/Node.h | 92 - .../llvm/ThreadRegions/Nodes/NodeIterator.h | 31 - include/dg/llvm/ThreadRegions/Nodes/Nodes.h | 102 - .../dg/llvm/ThreadRegions/Nodes/ReturnNode.h | 12 - .../dg/llvm/ThreadRegions/Nodes/UnlockNode.h | 12 - include/dg/llvm/ThreadRegions/ThreadRegion.h | 60 - include/dg/llvm/ValueRelations/GraphBuilder.h | 184 -- .../dg/llvm/ValueRelations/GraphElements.h | 243 --- .../llvm/ValueRelations/RelationsAnalyzer.h | 1153 ----------- .../llvm/ValueRelations/StructureAnalyzer.h | 794 -------- .../dg/llvm/ValueRelations/ValueRelations.h | 1803 ----------------- include/dg/llvm/ValueRelations/getValName.h | 51 - include/dg/llvm/llvm-utils.h | 215 -- include/dg/util/SilenceLLVMWarnings.h | 35 - include/dg/util/cow_shared_ptr.h | 80 - include/dg/util/debug.h | 117 -- include/dg/util/iterators.h | 34 - 163 files changed, 28944 deletions(-) delete mode 100644 include/dg/ADT/Bits.h delete mode 100644 include/dg/ADT/Bitvector.h delete mode 100644 include/dg/ADT/DGContainer.h delete mode 100644 include/dg/ADT/DisjunctiveIntervalMap.h delete mode 100644 include/dg/ADT/HashMap.h delete mode 100644 include/dg/ADT/HashMapImpl.h delete mode 100644 include/dg/ADT/IntervalsList.h delete mode 100644 include/dg/ADT/Map.h delete mode 100644 include/dg/ADT/NumberSet.h delete mode 100644 include/dg/ADT/Queue.h delete mode 100644 include/dg/ADT/STLHashMap.h delete mode 100644 include/dg/ADT/SetQueue.h delete mode 100644 include/dg/ADT/TslHopscotchHashMap.h delete mode 100644 include/dg/AnalysisOptions.h delete mode 100644 include/dg/BBlock.h delete mode 100644 include/dg/BBlockBase.h delete mode 100644 include/dg/BBlocksBuilder.h delete mode 100644 include/dg/BFS.h delete mode 100644 include/dg/CallGraph/CallGraph.h delete mode 100644 include/dg/ControlDependence/CDGraph.h delete mode 100644 include/dg/ControlDependence/ControlClosure.h delete mode 100644 include/dg/ControlDependence/ControlDependence.h delete mode 100644 include/dg/ControlDependence/ControlDependenceAnalysisOptions.h delete mode 100644 include/dg/ControlDependence/DOD.h delete mode 100644 include/dg/ControlDependence/DODNTSCD.h delete mode 100644 include/dg/ControlDependence/NTSCD.h delete mode 100644 include/dg/DFS.h delete mode 100644 include/dg/DG2Dot.h delete mode 100644 include/dg/DGParameters.h delete mode 100644 include/dg/DataDependence/DataDependence.h delete mode 100644 include/dg/DataDependence/DataDependenceAnalysisImpl.h delete mode 100644 include/dg/DataDependence/DataDependenceAnalysisOptions.h delete mode 100644 include/dg/DependenceGraph.h delete mode 100644 include/dg/Dominators/DominanceFrontiers.h delete mode 100644 include/dg/Dominators/PostDominanceFrontiers.h delete mode 100644 include/dg/MemorySSA/Definitions.h delete mode 100644 include/dg/MemorySSA/DefinitionsMap.h delete mode 100644 include/dg/MemorySSA/MemorySSA.h delete mode 100644 include/dg/MemorySSA/ModRef.h delete mode 100644 include/dg/MemoryState.h delete mode 100644 include/dg/Node.h delete mode 100644 include/dg/NodesWalk.h delete mode 100644 include/dg/Offset.h delete mode 100644 include/dg/PointerAnalysis/MemoryObject.h delete mode 100644 include/dg/PointerAnalysis/PSNode.h delete mode 100644 include/dg/PointerAnalysis/Pointer.h delete mode 100644 include/dg/PointerAnalysis/PointerAnalysis.h delete mode 100644 include/dg/PointerAnalysis/PointerAnalysisFI.h delete mode 100644 include/dg/PointerAnalysis/PointerAnalysisFS.h delete mode 100644 include/dg/PointerAnalysis/PointerAnalysisFSInv.h delete mode 100644 include/dg/PointerAnalysis/PointerAnalysisOptions.h delete mode 100644 include/dg/PointerAnalysis/PointerGraph.h delete mode 100644 include/dg/PointerAnalysis/PointerGraphOptimizations.h delete mode 100644 include/dg/PointerAnalysis/PointerGraphValidator.h delete mode 100644 include/dg/PointerAnalysis/PointsToMapping.h delete mode 100644 include/dg/PointerAnalysis/PointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/AlignedPointerIdPointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/AlignedSmallOffsetsPointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/LookupTable.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/OffsetsSetPointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/PointerIdPointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/SeparateOffsetsPointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/SimplePointsToSet.h delete mode 100644 include/dg/PointerAnalysis/PointsToSets/SmallOffsetsPointsToSet.h delete mode 100644 include/dg/ReadWriteGraph/DefSite.h delete mode 100644 include/dg/ReadWriteGraph/RWBBlock.h delete mode 100644 include/dg/ReadWriteGraph/RWNode.h delete mode 100644 include/dg/ReadWriteGraph/RWSubgraph.h delete mode 100644 include/dg/ReadWriteGraph/ReadWriteGraph.h delete mode 100644 include/dg/SCC.h delete mode 100644 include/dg/Slicing.h delete mode 100644 include/dg/SubgraphBase.h delete mode 100644 include/dg/SubgraphNode.h delete mode 100644 include/dg/SystemDependenceGraph/DGArgumentPair.h delete mode 100644 include/dg/SystemDependenceGraph/DGBBlock.h delete mode 100644 include/dg/SystemDependenceGraph/DGElement.h delete mode 100644 include/dg/SystemDependenceGraph/DGNode.h delete mode 100644 include/dg/SystemDependenceGraph/DGNodeCall.h delete mode 100644 include/dg/SystemDependenceGraph/DGParameters.h delete mode 100644 include/dg/SystemDependenceGraph/DepDGElement.h delete mode 100644 include/dg/SystemDependenceGraph/DependenceGraph.h delete mode 100644 include/dg/SystemDependenceGraph/SystemDependenceGraph.h delete mode 100644 include/dg/legacy/Analysis.h delete mode 100644 include/dg/legacy/BFS.h delete mode 100644 include/dg/legacy/DFS.h delete mode 100644 include/dg/legacy/DataFlowAnalysis.h delete mode 100644 include/dg/legacy/NodesWalk.h delete mode 100644 include/dg/llvm/CallGraph/CallGraph.h delete mode 100644 include/dg/llvm/ControlDependence/ControlClosure.h delete mode 100644 include/dg/llvm/ControlDependence/ControlDependence.h delete mode 100644 include/dg/llvm/ControlDependence/DOD.h delete mode 100644 include/dg/llvm/ControlDependence/GraphBuilder.h delete mode 100644 include/dg/llvm/ControlDependence/IGraphBuilder.h delete mode 100644 include/dg/llvm/ControlDependence/InterproceduralCD.h delete mode 100644 include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h delete mode 100644 include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h delete mode 100644 include/dg/llvm/ControlDependence/NTSCD.h delete mode 100644 include/dg/llvm/ControlDependence/SCD.h delete mode 100644 include/dg/llvm/ControlDependence/legacy/Block.h delete mode 100644 include/dg/llvm/ControlDependence/legacy/Function.h delete mode 100644 include/dg/llvm/ControlDependence/legacy/GraphBuilder.h delete mode 100644 include/dg/llvm/ControlDependence/legacy/NTSCD.h delete mode 100644 include/dg/llvm/ControlDependence/legacy/TarjanAnalysis.h delete mode 100644 include/dg/llvm/DataDependence/DataDependence.h delete mode 100644 include/dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h delete mode 100644 include/dg/llvm/DefUse/DefUse.h delete mode 100644 include/dg/llvm/Dominators/Dominators.h delete mode 100644 include/dg/llvm/ForkJoin/ForkJoin.h delete mode 100644 include/dg/llvm/GraphBuilder.h delete mode 100644 include/dg/llvm/LLVMAnalysisOptions.h delete mode 100644 include/dg/llvm/LLVMDG2Dot.h delete mode 100644 include/dg/llvm/LLVMDGAssemblyAnnotationWriter.h delete mode 100644 include/dg/llvm/LLVMDGVerifier.h delete mode 100644 include/dg/llvm/LLVMDependenceGraph.h delete mode 100644 include/dg/llvm/LLVMDependenceGraphBuilder.h delete mode 100644 include/dg/llvm/LLVMNode.h delete mode 100644 include/dg/llvm/LLVMSlicer.h delete mode 100644 include/dg/llvm/PointerAnalysis/DGPointerAnalysis.h delete mode 100644 include/dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h delete mode 100644 include/dg/llvm/PointerAnalysis/LLVMPointsToSet.h delete mode 100644 include/dg/llvm/PointerAnalysis/PointerAnalysis.h delete mode 100644 include/dg/llvm/PointerAnalysis/PointerGraph.h delete mode 100644 include/dg/llvm/PointerAnalysis/PointerGraphValidator.h delete mode 100644 include/dg/llvm/PointerAnalysis/SVFPointerAnalysis.h delete mode 100644 include/dg/llvm/ReadWriteGraph/LLVMReadWriteGraphBuilder.h delete mode 100644 include/dg/llvm/SystemDependenceGraph/AnnotationWriter.h delete mode 100644 include/dg/llvm/SystemDependenceGraph/SDG2Dot.h delete mode 100644 include/dg/llvm/SystemDependenceGraph/SystemDependenceGraph.h delete mode 100644 include/dg/llvm/SystemDependenceGraph/SystemDependenceGraphBuilder.h delete mode 100644 include/dg/llvm/ThreadRegions/ControlFlowGraph.h delete mode 100644 include/dg/llvm/ThreadRegions/Graphs/BlockGraph.h delete mode 100644 include/dg/llvm/ThreadRegions/Graphs/CriticalSectionsBuilder.h delete mode 100644 include/dg/llvm/ThreadRegions/Graphs/FunctionGraph.h delete mode 100644 include/dg/llvm/ThreadRegions/Graphs/GraphBuilder.h delete mode 100644 include/dg/llvm/ThreadRegions/Graphs/ThreadRegionsBuilder.h delete mode 100644 include/dg/llvm/ThreadRegions/MayHappenInParallel.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/CallFuncPtrNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/CallNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/CallReturnNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/EntryNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/ExitNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/ForkNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/GeneralNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/JoinNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/LockNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/Node.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/NodeIterator.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/Nodes.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/ReturnNode.h delete mode 100644 include/dg/llvm/ThreadRegions/Nodes/UnlockNode.h delete mode 100644 include/dg/llvm/ThreadRegions/ThreadRegion.h delete mode 100644 include/dg/llvm/ValueRelations/GraphBuilder.h delete mode 100644 include/dg/llvm/ValueRelations/GraphElements.h delete mode 100644 include/dg/llvm/ValueRelations/RelationsAnalyzer.h delete mode 100644 include/dg/llvm/ValueRelations/StructureAnalyzer.h delete mode 100644 include/dg/llvm/ValueRelations/ValueRelations.h delete mode 100644 include/dg/llvm/ValueRelations/getValName.h delete mode 100644 include/dg/llvm/llvm-utils.h delete mode 100644 include/dg/util/SilenceLLVMWarnings.h delete mode 100644 include/dg/util/cow_shared_ptr.h delete mode 100644 include/dg/util/debug.h delete mode 100644 include/dg/util/iterators.h diff --git a/.gitignore b/.gitignore index 6eb1f120a..f81e8375a 100644 --- a/.gitignore +++ b/.gitignore @@ -18,7 +18,6 @@ autom4te.cache !benchmarks/lighttpd-deps/pcre-8.36.tar.gz **/CMakeFiles/ **/CMakeCache.txt -/dg/ **/*.log *~ *.ll diff --git a/include/dg/ADT/Bits.h b/include/dg/ADT/Bits.h deleted file mode 100644 index 758f54f45..000000000 --- a/include/dg/ADT/Bits.h +++ /dev/null @@ -1,236 +0,0 @@ -#ifndef _DG_SPARSE_BITS_H_ -#define _DG_SPARSE_BITS_H_ - -#include // size_t -#include -#include - -namespace dg { -namespace ADT { - -// an element that holds a sequence of bits (up to 64 bits usually) -// along with offset. So this class can represent a sequence -// of [S, S(+sizeof(InnerT)*8)] bits. (E.g. 100th, 101th, ..., 163th bit) -template -class ShiftedBits { - InnerT _bits{0}; - ShiftT _shift; - -public: - ShiftedBits(ShiftT shift): _shift(shift) {} - - static size_t bitsNum() { return sizeof(InnerT) * 8; } - bool operator==(const ShiftedBits& rhs) const { return _bits == rhs._bits && _shift == rhs._shift; } - bool operator!=(const ShiftedBits& rhs) const { return !operator==(rhs); } - - bool mayContain(size_t i) const { - return i >= _shift && i - _shift < bitsNum(); - } - - size_t size() const { - size_t num = 0; - for (size_t i = 0; i < bitsNum(); ++i) - if (_bits & (static_cast(1) << i)) - ++num; - - return num; - } - - bool empty() const { return _bits == 0; } - - bool get(size_t i) const { - if (!mayContain(i)) - return false; - - assert(i - _shift < bitsNum()); - return _bits & (static_cast(1) << (i - _shift)); - } - - // return the previous value - bool set(size_t i) { - assert(mayContain(i)); - bool ret = get(i); - _bits |= (static_cast(1) << (i - _shift)); - return ret; - } - - class const_iterator { - const ShiftedBits *bits{nullptr}; - size_t pos{0}; - - bool isEnd() const { - return pos == ShiftedBits::bitsNum(); - } - - void _findNext() { - assert(pos < ShiftedBits::bitsNum()); - while (!(bits->_bits & (static_cast(1) << pos))) { - ++pos; - - if (isEnd()) - return; - } - } - - const_iterator(const ShiftedBits *bits, size_t pos = 0) - :bits(bits), pos(pos) { - assert(bits && "No bits given"); - // start at the first element - if (!isEnd() && !(bits->_bits & 1)) - operator++(); - } - - public: - const_iterator() = default; - const_iterator(const const_iterator&) = default; - const_iterator& operator=(const const_iterator&) = default; - - const_iterator& operator++() { - ++pos; - if (!isEnd()) - _findNext(); - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - size_t operator*() const { - assert(pos < ShiftedBits::bitsNum()); - assert(bits->mayContain(bits->_shift + pos)); - return bits->_shift + pos; - } - - bool operator==(const const_iterator& rhs) const { - return pos == rhs.pos && bits == rhs.bits; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class ShiftedBits; - }; - - const_iterator begin() const { return const_iterator(this); } - const_iterator end() const { return const_iterator(this, bitsNum()); } - - friend class const_iterator; -}; - -// a set of bits with 0 offset. This is a special case -// of ShiftedBits with _shift = 0 -template -class Bits { - InnerT _bits{0}; - -public: - static size_t bitsNum() { return sizeof(InnerT) * 8; } - bool operator==(const Bits& rhs) const { return _bits == rhs._bits; } - bool operator!=(const Bits& rhs) const { return !operator==(rhs); } - - bool empty() const { return _bits == 0; } - bool mayContain(size_t i) const { return i < bitsNum(); } - - size_t size() const { - size_t num = 0; - for (size_t i = 0; i < bitsNum(); ++i) - if (_bits & (static_cast(1) << i)) - ++num; - - return num; - } - - bool get(size_t i) const { - if (!mayContain(i)) - return false; - - assert(i < bitsNum()); - return _bits & (static_cast(1) << i); - } - - // return the previous value - bool set(size_t i) { - assert(mayContain(i)); - bool ret = get(i); - _bits |= (static_cast(1) << i); - return ret; - } - - class const_iterator { - const Bits *bits{nullptr}; - size_t pos{0}; - - bool isEnd() const { - return pos == Bits::bitsNum(); - } - - void _findNext() { - assert(pos < Bits::bitsNum()); - while (!(bits->_bits & (static_cast(1) << pos))) { - ++pos; - - if (isEnd()) - return; - } - } - - const_iterator(const Bits *bits, size_t pos = 0) - :bits(bits), pos(pos) { - assert(bits && "No bits given"); - // start at the first element - if (!isEnd() && !(bits->_bits & 1)) - operator++(); - } - - public: - const_iterator() = default; - const_iterator(const const_iterator&) = default; - const_iterator& operator=(const const_iterator&) = default; - - const_iterator& operator++() { - ++pos; - if (!isEnd()) - _findNext(); - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - size_t operator*() const { - assert(pos < Bits::bitsNum()); - assert(bits->mayContain(pos)); - return pos; - } - - bool operator==(const const_iterator& rhs) const { - return pos == rhs.pos && bits == rhs.bits; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class Bits; - }; - - const_iterator begin() const { return const_iterator(this); } - const_iterator end() const { return const_iterator(this, bitsNum()); } - - friend class const_iterator; -}; - - - -} // namespace ADT -} // namespace dg - -#endif // _DG_SPARSE_BITS_H_ diff --git a/include/dg/ADT/Bitvector.h b/include/dg/ADT/Bitvector.h deleted file mode 100644 index c28ea7f56..000000000 --- a/include/dg/ADT/Bitvector.h +++ /dev/null @@ -1,259 +0,0 @@ -#ifndef _DG_SPARSE_BITVECTOR_H_ -#define _DG_SPARSE_BITVECTOR_H_ - -#include -#include - -#include "HashMap.h" -#include "Map.h" - -namespace dg { -namespace ADT { - -template > -class SparseBitvectorImpl { - // mapping from shift to bits - BitsContainerT _bits{}; - - static size_t _bitsNum() { return sizeof(BitsT) * 8; } - static ShiftT _shift(IndexT i) { return i - (i % _bitsNum()); } - - static size_t _countBits(BitsT bits) { - size_t num = 0; - while (bits) { - if (bits & 0x1) - ++num; - - bits = bits >> 1; - } - - return num; - } - - void _addBit(IndexT i) { - // for now we just push it back, - // but we would rather do it somehow - // more smartly (probably not using the vector) - auto sft = _shift(i); - _bits.emplace(sft, BitsT{1} << (i - sft)); - } - -public: - SparseBitvectorImpl() = default; - SparseBitvectorImpl(IndexT i) { _addBit(i); } // singleton ctor - - SparseBitvectorImpl(const SparseBitvectorImpl&) = default; - SparseBitvectorImpl(SparseBitvectorImpl&&) = default; - - void reset() { _bits.clear(); } - bool empty() const { return _bits.empty(); } - void swap(SparseBitvectorImpl& oth) { _bits.swap(oth._bits); } - - // TODO: use SFINAE to define empty body if a class without - // reserve() is used... - void reserve(size_t n) { _bits.reserve(n); } - - bool get(IndexT i) const { - auto sft = _shift(i); - assert(sft % _bitsNum() == 0); - - auto it = _bits.find(sft); - if (it == _bits.end()) { - return false; - } - - return (it->second & (BitsT{1} << (i - sft))); - } - - // returns the previous value of the i-th bit - bool set(IndexT i) { - auto sft = _shift(i); - auto& B = _bits[sft]; - - bool prev = B & (BitsT{1} << (i - sft)); - B |= BitsT{1} << (i - sft); - - return prev; - } - - // union operation - bool set(const SparseBitvectorImpl& rhs) { - bool changed = false; - for (auto& pair : rhs._bits) { - auto& B = _bits[pair.first]; - auto old = B; - B |= pair.second; - changed |= old != B; - } - - return changed; - } - - // returns the previous value of the i-th bit - bool unset(IndexT i) { - auto sft = _shift(i); - auto it = _bits.find(sft); - if (it == _bits.end()) { - return false; - } - - // FIXME: use this implementation only for hash map - // which returns read-only object and - // modify directly it->second in other cases (for std::map) - auto res = it->second & ~(BitsT{1} << (i - sft)); - if (res == 0) { - _bits.erase(it); - } else { - _bits[sft] = res; - } - - return true; - } - - // FIXME: track the number of elements - // in a variable, to avoid this search... - size_t size() const { - size_t num = 0; - for (auto& it : _bits) - num += _countBits(it.second); - - return num; - } - - class const_iterator { - typename BitsContainerT::const_iterator container_it; - typename BitsContainerT::const_iterator container_end; - size_t pos{0}; - - const_iterator(const BitsContainerT& cont, bool end = false) - :container_it(end ? cont.end() : cont.begin()), - container_end(cont.end()) { - // set-up the initial position - if (!end && !cont.empty()) - _findClosestBit(); - } - - void _findClosestBit() { - assert(pos < (sizeof(BitsT)*8)); - while (!(container_it->second & (BitsT{1} << pos))) { - if (++pos == sizeof(BitsT)*8) - return; - } - } - - /* - * This implementation seems less efficient, - * but let's keep it commented here for - * further experiments - void _findClosestBit() { - assert(pos < (sizeof(BitsT)*8)); - BitsT tmp = container_it->second; - - // shift tmp to start at position pos - tmp >>= pos; - - if (tmp == 0) { - pos = sizeof(BitsT)*8; - assert(pos <= (sizeof(BitsT)*8)); - return; - } - - int i = 0; - while (tmp) { - // pos bit set - if (tmp & 0x1) { - pos += i; - assert(pos < (sizeof(BitsT)*8)); - assert(container_it->second & (BitsT{1} << pos)); - return; - } - - if (tmp & 0xff) { - if (tmp & 0xf) { - // we tested this condition - // before entering this code - assert(!(tmp & 0x1)); - if (tmp & 0x2) { - pos += i + 1; - assert(pos < (sizeof(BitsT)*8)); - assert(container_it->second & (BitsT{1} << pos)); - return; - } else { - assert(!(tmp & 0x3)); - tmp >>= 2; - i += 2; - } - } else { - tmp >>= 4; - i += 4; - } - } else { - tmp >>= 8; - i += 8; - } - } - - // not found - pos = sizeof(BitsT)*8; - } - */ - - public: - const_iterator() = default; - const_iterator& operator++() { - // shift to the next bit in the current bits - assert(pos < (sizeof(BitsT)*8)); - assert(container_it != container_end && "operator++ called on end"); - if (++pos != 64) - _findClosestBit(); - - if (pos == 64) { - ++container_it; - pos = 0; - if (container_it != container_end) { - assert(container_it->second != 0 && "Empty bucket in a bitvector"); - _findClosestBit(); - } - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - IndexT operator*() const { - return container_it->first + pos; - } - - bool operator==(const const_iterator& rhs) const { - return pos == rhs.pos && container_it == rhs.container_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class SparseBitvectorImpl; - }; - - const_iterator begin() const { return const_iterator(_bits); } - const_iterator end() const { return const_iterator(_bits, true /* end */); } - - friend class const_iterator; -}; - -using SparseBitvectorMapImpl = SparseBitvectorImpl>; -using SparseBitvectorHashImpl = SparseBitvectorImpl>; -using SparseBitvector = SparseBitvectorMapImpl; - -} // namespace ADT -} // namespace dg - -#endif // _DG_SPARSE_BITVECTOR_H_ diff --git a/include/dg/ADT/DGContainer.h b/include/dg/ADT/DGContainer.h deleted file mode 100644 index cd28cf2f3..000000000 --- a/include/dg/ADT/DGContainer.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef _DG_CONTAINER_H_ -#define _DG_CONTAINER_H_ - -#include -#include -#include - -namespace dg { - -/// ------------------------------------------------------------------ -// - DGContainer -// -// This is basically just a wrapper for real container, so that -// we have the container defined on one place for all edges. -// It may have more implementations depending on available features -/// ------------------------------------------------------------------ -template -class DGContainer -{ -public: - // XXX use llvm ADTs when available, or BDDs? - using ContainerT = typename std::set; - using iterator = typename ContainerT::iterator; - using const_iterator = typename ContainerT::const_iterator; - using size_type = typename ContainerT::size_type; - - iterator begin() { return container.begin(); } - const_iterator begin() const { return container.begin(); } - iterator end() { return container.end(); } - const_iterator end() const { return container.end(); } - - size_type size() const - { - return container.size(); - } - - bool insert(ValueT n) - { - return container.insert(n).second; - } - - bool contains(ValueT n) const - { - return container.count(n) != 0; - } - - size_t erase(ValueT n) - { - return container.erase(n); - } - - void clear() - { - container.clear(); - } - - bool empty() - { - return container.empty(); - } - - void swap(DGContainer& oth) - { - container.swap(oth.container); - } - - void intersect(const DGContainer& oth) - { - DGContainer tmp; - - std::set_intersection(container.begin(), container.end(), - oth.container.begin(), - oth.container.end(), - std::inserter(tmp.container, - tmp.container.begin())); - - // swap containers - container.swap(tmp.container); - } - - bool operator==(const DGContainer& oth) const - { - if (container.size() != oth.size()) - return false; - - // the sets are ordered, so this will work - iterator snd = oth.container.begin(); - for (iterator fst = container.begin(), efst = container.end(); - fst != efst; ++fst, ++snd) - if (*fst != *snd) - return false; - - return true; - } - - bool operator!=(const DGContainer& oth) const - { - return !operator==(oth); - } - -private: - ContainerT container; -}; - -// Edges are pointers to other nodes -template -class EdgesContainer : public DGContainer -{ -}; - -} // namespace dg - -#endif // _DG_CONTAINER_H_ diff --git a/include/dg/ADT/DisjunctiveIntervalMap.h b/include/dg/ADT/DisjunctiveIntervalMap.h deleted file mode 100644 index a461294ab..000000000 --- a/include/dg/ADT/DisjunctiveIntervalMap.h +++ /dev/null @@ -1,648 +0,0 @@ -#ifndef DG_DISJUNCTIVE_INTERVAL_MAP_H_ -#define DG_DISJUNCTIVE_INTERVAL_MAP_H_ - -#include -#include -#include -#include -#include - -#ifndef NDEBUG -#include -#endif - -#include "dg/Offset.h" - -namespace dg { -namespace ADT { - -template -struct DiscreteInterval { - using ValueT = T; - - T start; - T end; - - DiscreteInterval(T s, T e) : start(s), end(e) { - assert(s <= e && "Invalid interval"); - } - - T length() const { - // +1 as the intervals are discrete - // (interval |0|1|2|3| has length 4) - return end - start + 1; - } - - // total order on intervals so that we can insert them - // to std containers. We want to compare them only - // according to the start value. - bool operator<(const DiscreteInterval& I) const { - return start < I.start; - } - - bool operator==(const DiscreteInterval& I) const { - return start == I.start && end == I.end; - } - - bool operator!=(const DiscreteInterval& I) const { - return !operator==(I); - } - - bool overlaps(const DiscreteInterval& I) const { - return (start <= I.start && end >= I.start) || I.end >= start; - } - - bool covers(const DiscreteInterval& I) const { - return (start <= I.start && end >= I.end); - } - - bool overlaps(T rhs_start, T rhs_end) const { - return overlaps(DiscreteInterval(rhs_start, rhs_end)); - } - - bool covers(T rhs_start, T rhs_end) const { - return covers(DiscreteInterval(rhs_start, rhs_end)); - } - -#ifndef NDEBUG - void dump() const; -#endif -}; - - -/// -// Mapping of disjunctive discrete intervals of values -// to sets of ValueT. -template -class DisjunctiveIntervalMap { -public: - using IntervalT = DiscreteInterval; - using ValuesT = std::set; - using MappingT = std::map; - using iterator = typename MappingT::iterator; - using const_iterator = typename MappingT::const_iterator; - - /// - // Return true if the mapping is updated anyhow - // (intervals split, value added). - bool add(const IntervalValueT start, const IntervalValueT end, - const ValueT& val) { - return add(IntervalT(start, end), val); - } - - bool add(const IntervalT& I, const ValueT& val) { - return _add(I, val, false); - } - - template - bool add(const IntervalT& I, const ContT& vals) { - bool changed = false; - for (const ValueT& val : vals) { - changed |= _add(I, val, false); - } - return changed; - } - - bool update(const IntervalValueT start, const IntervalValueT end, - const ValueT& val) { - return update(IntervalT(start, end), val); - } - - bool update(const IntervalT& I, const ValueT& val) { - return _add(I, val, true); - } - - template - bool update(const IntervalT& I, const ContT& vals) { - bool changed = false; - for (const ValueT& val : vals) { - changed |= _add(I, val, true); - } - return changed; - } - - // add the value 'val' to all intervals - bool addAll(const ValueT& val) { - bool changed = false; - for (auto& it : _mapping) { - changed |= it.second.insert(val).second; - } - return changed; - } - - // return true if some intervals from the map - // has a overlap with I - bool overlaps(const IntervalT& I) const { - if (_mapping.empty()) - return false; - - auto ge = _find_ge(I); - if (ge == _mapping.end()) { - auto last = _get_last(); - return last->first.end >= I.start; - } else { - return ge->first.start <= I.end; - } - } - - bool overlaps(IntervalValueT start, IntervalValueT end) const { - return overlaps(IntervalT(start, end)); - } - - // return true if the map has an entry for - // each single byte from the interval I - bool overlapsFull(const IntervalT& I) const { - if (_mapping.empty()) { - return false; - } - - auto ge = _find_ge(I); - if (ge == _mapping.end()) { - auto last = _get_last(); - return last->first.end >= I.end; - } else { - if (ge->first.start > I.start) { - if (ge == _mapping.begin()) - return false; - auto prev = ge; - --prev; - if (prev->first.end != ge->first.start - 1) - return false; - } - - IntervalValueT last_end = ge->first.end; - while (ge->first.end < I.end) { - ++ge; - if (ge == _mapping.end()) - return false; - - if (ge->first.start != last_end + 1) - return false; - - last_end = ge->first.end; - } - - // full overlap means that there are not uncovered bytes - assert(uncovered(I) == decltype(uncovered(I)){}); - return true; - } - } - - bool overlapsFull(IntervalValueT start, IntervalValueT end) const { - return overlapsFull(IntervalT(start, end)); - } - - DisjunctiveIntervalMap intersection(const DisjunctiveIntervalMap& rhs) const { - DisjunctiveIntervalMap tmp; - // FIXME: this could be done more efficiently - auto it = _mapping.begin(); - for (auto& rhsit : rhs._mapping) { - while (it->first.end < rhsit.first.start) { - if (it == _mapping.end()) { - return tmp; - } - } - if (it->first.overlaps(rhsit.first)) { - decltype(rhsit.second) vals; - std::set_intersection(it->second.begin(), it->second.end(), - rhsit.second.begin(), rhsit.second.end(), - std::inserter(vals, vals.begin())); - tmp.add(IntervalT{std::max(it->first.start, rhsit.first.start), - std::min(it->first.end, rhsit.first.end)}, - vals); - } - } - return tmp; - } - - /// - // Gather all values that are covered by the interval I - std::set gather(IntervalValueT start, IntervalValueT end) const { - return gather(IntervalT(start, end)); - } - - std::set gather(const IntervalT& I) const { - std::set ret; - - auto it = le(I); - if (it == end()) - return ret; - - assert(it->first.overlaps(I) && "The found interval should overlap"); - while (it != end() && it->first.overlaps(I)) { - ret.insert(it->second.begin(), it->second.end()); - ++it; - } - - return ret; - } - - std::vector uncovered(IntervalValueT start, IntervalValueT end) const { - return uncovered(IntervalT(start, end)); - } - - std::vector uncovered(const IntervalT& I) const { - if (_mapping.empty()) - return {I}; - - auto it = le(I); - if (it == end()) - return {I}; - - std::vector ret; - IntervalT cur = I; - - if (cur.start > it->first.start) { - // the first interval covers the whole I? - if (it->first.end >= I.end) { - return {}; - } - - assert(cur == I); - cur.start = it->first.end + 1; - assert(cur.end == I.end); - assert(I.end > it->first.end); - // we handled this interval, move further - ++it; - - // nothing else to handle... - if (it == _mapping.end()) { - if (cur.start <= cur.end) - return {cur}; - } - } - - while (true) { - assert(cur.start <= it->first.start); - if (cur.start != it->first.start && - cur.start < it->first.start) { - assert(it->first.start != 0 && "Underflow"); - ret.push_back(IntervalT{cur.start, it->first.start - 1}); - } - // does the rest of the interval covers all? - if (it->first.end >= I.end) - break; - - assert(it->first.end != (~static_cast(0)) - && "Overflow"); - cur.start = it->first.end + 1; - assert(cur.end == I.end); - - ++it; - if (it == end()) { - if (cur.start <= cur.end) - ret.push_back(cur); - // we're done here - break; - } - } - - return ret; - } - - bool empty() const { return _mapping.empty(); } - size_t size() const { return _mapping.size(); } - - iterator begin() { return _mapping.begin(); } - const_iterator begin() const { return _mapping.begin(); } - iterator end() { return _mapping.end(); } - const_iterator end() const { return _mapping.end(); } - - bool operator==(const DisjunctiveIntervalMap& rhs) const { - return _mapping == rhs._mapping; - } - - // return the iterator to an element that is the first - // that overlaps the interval I or end() if there is - // no such interval - iterator le(const IntervalT& I) { - return _shift_le(_find_ge(I), I); - } - - const_iterator le(const IntervalT& I) const { - return _shift_le(_find_ge(I), I); - } - - iterator le(const IntervalValueT start, const IntervalValueT end) { - return le(IntervalT(start, end)); - } - - const_iterator le(const IntervalValueT start, const IntervalValueT end) const { - return le(IntervalT(start, end)); - } - -#ifndef NDEBUG - friend std::ostream& operator<<(std::ostream& os, const DisjunctiveIntervalMap& map) { - os << "{"; - for (const auto& pair : map) { - if (pair.second.empty()) - continue; - - os << "{ "; - os << pair.first.start << "-" << pair.first.end; - os << ": " << *pair.second.begin(); - os << " }, "; - } - os << "}"; - return os; - } - - void dump() const { std::cout << *this << "\n"; } -#endif - -#if 0 - friend llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const DisjunctiveIntervalMap& map) { - os << "{"; - for (const auto& pair : map) { - if (pair.second.empty()) - continue; - - os << "{ "; - os << *pair.first.start << "-" << *pair.first.end; - os << ": " << *pair.second.begin(); - os << " }, "; - } - os << "}"; - return os; - } - -#endif - -private: - - // shift the iterator such that it will point to the - // first element that overlaps with I, or to end - // if there is no such interval - template - IteratorT _shift_le(const IteratorT& startge, const IntervalT& I) const { - // find the element that starts at the same value - // as I or later (i.e. I.start >= it.start) - if (startge == end()) { - auto last = _get_last(); - if (last->first.end >= I.start) { - assert(last->first.overlaps(I)); - return last; - } - - return end(); - } - - assert(startge->first.start >= I.start); - - // check whether there's - // an previous interval with an overlap - if (startge != begin()) { - auto tmp = startge; - --tmp; - if (tmp->first.end >= I.start) { - assert(tmp->first.overlaps(I)); - return tmp; - } - // fall-through - } - - // starge is the first interval or the - // previous interval does not overlap. - // Just check whether this interval overlaps - if (startge->first.start > I.end) - return end(); - - assert(startge->first.overlaps(I)); - return startge; - } - - // Split interval [a,b] to two intervals [a, where] and [where + 1, b]. - // Each of the new intervals has a copy of the original set associated - // to the original interval. - // Returns the new lower interval - // XXX: could we pass just references to the iterator? - template - IteratorT splitIntervalHint(IteratorT I, IntervalValueT where, - HintIteratorT hint) { - auto interval = I->first; - auto values = std::move(I->second); - - assert(interval.start != interval.end && "Cannot split such interval"); - assert(interval.start <= where && where <= interval.end - && "Value 'where' must lie inside the interval"); - assert(where < interval.end && "The second interval would be empty"); - - // remove the original interval and replace it with - // two splitted intervals - _mapping.erase(I); - auto ret = - _mapping.emplace_hint(hint, IntervalT(interval.start, where), values); - _mapping.emplace_hint(hint, IntervalT(where + 1, interval.end), - std::move(values)); - return ret; - } - - bool splitExtBorders(const IntervalT& I) { - assert(!_mapping.empty()); - - auto ge = _mapping.lower_bound(I); - if (ge == _mapping.end()) { - // the last interval must start somewhere to the - // left from our new interval - auto last = _get_last(); - assert(last->first.start < I.start); - - bool changed = false; - if (last->first.end > I.end) { - last = splitIntervalHint(last, I.end, ge); - changed |= true; - } - - if (last->first.end >= I.start) { - splitIntervalHint(last, I.start - 1, ge); - changed |= true; - } - return changed; - } else { - // we found an interval starting at I.start - // or later - assert(ge->first.start >= I.start); - bool changed = false; - // FIXME: optimize this... - // add _find_le to find the "closest" interval from the right - // (maybe iterating like this is faster, though) - auto it = ge; - while (it->first.start <= I.end) { - if (it->first.end > I.end) { - it = splitIntervalHint(it, I.end, _mapping.end()); - changed = true; - break; - } - ++it; - if (it == _mapping.end()) - break; - } - - // we may have also overlap from the previous interval - if (changed) - ge = _mapping.lower_bound(I); - if (ge != _mapping.begin()) { - auto prev = ge; - --prev; - auto prev_end = prev->first.end; - if (prev_end >= I.start) { - ge = splitIntervalHint(prev, I.start - 1, ge); - changed = true; - } - // is the new interval entirely covered by the previous? - if (prev_end > I.end) { - // get the higher of the new intervals - ++ge; - assert(ge != _mapping.end()); - assert(ge->first.end == prev_end); -#ifndef NDEBUG - auto check = -#endif - splitIntervalHint(ge, I.end, _mapping.end()); - assert(check->first == I); - changed = true; - } - } - - return changed; - } - - return false; - } - - template - bool _addValue(IteratorT I, ValueT val, bool update) { - if (update) { - if (I->second.size() == 1 && I->second.count(val) > 0) - return false; - - I->second.clear(); - I->second.insert(val); - return true; - } - - return I->second.insert(val).second; - } - - // If the boolean 'update' is set to true, the value - // is not added, but rewritten - bool _add(const IntervalT& I, const ValueT& val, bool update = false) { - if (_mapping.empty()) { - _mapping.emplace(I, ValuesT{val}); - return true; - } - - // fast path - //auto fastit = _mapping.lower_bound(I); - //if (fastit != _mapping.end() && - // fastit->first == I) { - // return _addValue(fastit, val, update); - //} - - // XXX: pass the lower_bound iterator from the fast path - // so that we do not search the mapping again - bool changed = splitExtBorders(I); - _check(); - - // splitExtBorders() arranged the intervals such - // that some interval starts with ours - // and some interval ends with ours. - // Now we just create new intervals in the gaps - // and add values to the intervals that we have - - // FIXME: splitExtBorders() can return the iterator, - // so that we do not need to search the map again. - auto it = _find_ge(I); - assert(!changed || it != _mapping.end()); - - // we do not have any overlapping interval - if (it == _mapping.end() - || I.end < it->first.start) { - assert(!overlaps(I) && "Bug in add() or in overlaps()"); - _mapping.emplace(I, ValuesT{val}); - return true; - } - - auto rest = I; - assert(rest.end >= it->first.start); // we must have some overlap here - while (it != _mapping.end()) { - if (rest.start < it->first.start) { - // add the gap interval - _mapping.emplace_hint(it, IntervalT(rest.start, it->first.start - 1), - ValuesT{val}); - rest.start = it->first.start; - changed = true; - } else { - // update the existing interval and shift - // to the next interval - assert(rest.start == it->first.start); - changed |= _addValue(it, val, update); - if (it->first.end == rest.end) - break; - - rest.start = it->first.end + 1; - ++it; - - // our interval spans to the right - // after the last covered interval - if (it == _mapping.end() - || it->first.start > rest.end) { - _mapping.emplace_hint(it, rest, ValuesT{val}); - changed = true; - break; - } - } - } - - _check(); - return changed; - } - - // find the elements starting at - // or right to the interval - typename MappingT::iterator _find_ge(const IntervalT& I) { - // lower_bound = lower upper bound - return _mapping.lower_bound(I); - } - - typename MappingT::const_iterator _find_ge(const IntervalT& I) const { - return _mapping.lower_bound(I); - } - - typename MappingT::iterator _get_last() { - assert(!_mapping.empty()); - return (--_mapping.end()); - } - - typename MappingT::const_iterator _get_last() const { - assert(!_mapping.empty()); - return (--_mapping.end()); - } - - void _check() const { -#ifndef NDEBUG - // check that the keys are disjunctive - auto it = _mapping.begin(); - auto last = it->first; - ++it; - while (it != _mapping.end()) { - assert(last.start <= last.end); - // this one is nontrivial (the other assertions - // should be implied by the Interval and std::map propertis) - assert(last.end < it->first.start); - assert(it->first.start <= it->first.end); - - last = it->first; - ++it; - } -#endif // NDEBUG - } - - MappingT _mapping; -}; - - -} // namespace ADT -} // namespace dg - -#endif // _DG_DISJUNCTIVE_INTERVAL_MAP_H_ diff --git a/include/dg/ADT/HashMap.h b/include/dg/ADT/HashMap.h deleted file mode 100644 index 4472dea8a..000000000 --- a/include/dg/ADT/HashMap.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef DG_HASH_MAP_H_ -#define DG_HASH_MAP_H_ - -#ifdef HAVE_TSL_HOPSCOTCH -#include "TslHopscotchHashMap.h" -namespace dg { - template - using HashMap = HopscotchHashMap; -} -#else -#include "STLHashMap.h" -namespace dg { - template - using HashMap = STLHashMap; -} -#endif - -#endif diff --git a/include/dg/ADT/HashMapImpl.h b/include/dg/ADT/HashMapImpl.h deleted file mode 100644 index be6a98a97..000000000 --- a/include/dg/ADT/HashMapImpl.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef DG_HASH_MAP_IMPL_H_ -#define DG_HASH_MAP_IMPL_H_ - -namespace dg { - -template -class HashMapImpl : public Impl { -public: - using iterator = typename Impl::iterator; - using const_iterator = typename Impl::const_iterator; - - bool put(const Key& k, Val v) { - auto it = this->insert(std::make_pair(k, v)); - return it.second; - } - - const Val *get(const Key& k) const { - auto it = this->find(k); - if (it != this->end()) - return &it->second; - return nullptr; - } - - Val *get(Key& k) { - auto it = this->find(k); - if (it != this->end()) - return &it->second; - return nullptr; - } - -}; - -} // namespace dg - -#endif diff --git a/include/dg/ADT/IntervalsList.h b/include/dg/ADT/IntervalsList.h deleted file mode 100644 index c1de3c4d4..000000000 --- a/include/dg/ADT/IntervalsList.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef DG_INTERVALS_LIST_H_ -#define DG_INTERVALS_LIST_H_ - -#include -#include -#include "dg/Offset.h" - -namespace dg { -namespace dda { - -class IntervalsList { - struct Interval { - Offset start; - Offset end; - - Interval(Offset s, Offset e) : start(s), end(e) { - assert(start <= end); - } - - Interval(const std::pair& I) : Interval(I.first, I.second) {} - - bool overlaps(const Interval& I) const { - return start <= I.end && end >= I.start; - } - Offset length() const { return end - start + 1; } - }; - - std::list intervals; - - template - void _replace_overlapping(const Interval& I, Iterator it, Iterator to) { - assert(it->overlaps(I) && to->overlaps(I)); - assert(it != intervals.end()); - assert(to != intervals.end()); - it->start = std::min(it->start, I.start); - it->end = std::max(to->end, I.end); - - ++it; - ++to; - if (it != intervals.end()) { - intervals.erase(it, to); - } else { - assert(to == intervals.end()); - } - } - -#ifndef NDEBUG - bool _check() { - auto it = intervals.begin(); - if (it != intervals.end()) { - assert(it->start < it->end); - auto last = it++; - while (it != intervals.end()) { - assert(last->start < last->end); - assert(last->end < it->start); - assert(it->start < it->end); - } - } - return true; - } -#endif // NDEBUG - -public: - - void add(Offset start, Offset end) { - add({start, end}); - } - - void add(const Interval& I) { - if (intervals.empty()) - intervals.push_back(I); - - auto it = intervals.begin(); - auto end = intervals.end(); - - while (it != end) { - if (I.overlaps(*it)) { - auto to = ++it; - while (to != end && I.overlaps(*to)) { - ++to; - } - if (to == end) { - --to; - } - _replace_overlapping(I, it, to); - break; - } else if (it->start > I.end) { - intervals.insert(it, I); - break; - } - - ++it; - } - - if (it == end) { - intervals.push_back(I); - } - assert(_check()); - } - - IntervalsList& intersectWith(const IntervalsList& rhs) { - if (intervals.empty()) - return *this; - - auto it = intervals.begin(); - for (auto& RI : rhs.intervals) { - while (it->end < RI.start) { - auto tmp = it++; - intervals.erase(tmp); - if (it == intervals.end()) { - return *this; - } - } - if (it->overlaps(RI)) { - it->start = std::max(it->start, RI.start); - it->end = std::min(it->end, RI.end); - } - } - - return *this; - } - - auto begin() -> decltype (intervals.begin()) { return intervals.begin(); } - auto begin() const -> decltype (intervals.begin()) { return intervals.begin(); } - auto end() -> decltype (intervals.end()) { return intervals.end(); } - auto end()const -> decltype (intervals.end()) { return intervals.end(); } -}; - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/ADT/Map.h b/include/dg/ADT/Map.h deleted file mode 100644 index 2b16ac467..000000000 --- a/include/dg/ADT/Map.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef DG_MAP_IMPL_H_ -#define DG_MAP_IMPL_H_ - -#include - -namespace dg { - -// an ordered map with a unified API -template -class MapImpl : public Impl { -public: - using iterator = typename Impl::iterator; - using const_iterator = typename Impl::const_iterator; - - bool put(const Key& k, Val v) { - auto it = this->insert(std::make_pair(k, v)); - return it.second; - } - - const Val *get(const Key& k) const { - auto it = this->find(k); - if (it != this->end()) - return &it->second; - return nullptr; - } - - Val *get(Key& k) { - auto it = this->find(k); - if (it != this->end()) - return &it->second; - return nullptr; - } - - void reserve(size_t) { - // so that we can exchangabily use with HashMap - } -}; - -template -using Map = MapImpl>; - -} // namespace dg - -#endif diff --git a/include/dg/ADT/NumberSet.h b/include/dg/ADT/NumberSet.h deleted file mode 100644 index c1b426e5b..000000000 --- a/include/dg/ADT/NumberSet.h +++ /dev/null @@ -1,283 +0,0 @@ -#ifndef _DG_NUMBER_SET_H_ -#define _DG_NUMBER_SET_H_ - -#include "Bits.h" -#include "Bitvector.h" - -namespace dg { -namespace ADT { - -// this is just a wrapper around sparse bitvector -// that translates the bitvector methods to a new methods. -// There is no possibility to remove elements from the set. -class BitvectorNumberSet { - using NumT = uint64_t; - using ContainerT = SparseBitvectorImpl; - - ContainerT _bitvector; -public: - using const_iterator = typename ContainerT::const_iterator; - - BitvectorNumberSet() = default; - BitvectorNumberSet(size_t n) :_bitvector(n) {}; - BitvectorNumberSet(BitvectorNumberSet&&) = default; - - bool add(NumT n) { return !_bitvector.set(n); } - bool has(NumT n) const { return _bitvector.get(n); } - bool empty() const { return _bitvector.empty(); } - size_t size() const { return _bitvector.size(); } - void swap(BitvectorNumberSet& oth) { oth._bitvector.swap(_bitvector); } - - const_iterator begin() const { return _bitvector.begin(); } - const_iterator end() const { return _bitvector.end(); } -}; - - -// This class is a container for a set of numbers -// that is optimized for holding small values -// (values less than sizeof(NumT)*8*SmallElemNum)). -// If a greater value is inserted, the whole container -// is lifted to a normal set. -// There is no possibility to remove elements from the set. -class SmallNumberSet { - using NumT = uint64_t; - // NOTE: if we change this to something that - // has non-trivial ctors/dtors, we must reflect it in the code! - using SmallSetT = Bits; - using BigSetT = BitvectorNumberSet; - - bool is_small{true}; - - union SetT { - SmallSetT small; - BigSetT big; - - SetT() : small() {} - ~SetT() {} - } _set; - - void _lift(NumT n) { - assert(is_small); - - auto S = BigSetT(n); - for (auto x : _set.small) - S.add(x); - - // initialize the big-set - new (&_set.big) BigSetT(); - S.swap(_set.big); - - is_small = false; - } - -public: - ~SmallNumberSet() { - // if we used the big set, call its destructor - // (for smalls set there's nothing to do) - if (!is_small) - _set.big.~BigSetT(); - } - - bool add(NumT n) { - if (is_small) { - if (_set.small.mayContain(n)) - return !_set.small.set(n); - else { - _lift(n); - return true; - } - } else - return _set.big.add(n); - } - - bool has(NumT n) const { - return is_small ? _set.small.get(n) : _set.big.has(n); - } - - bool empty() const { - return is_small ? _set.small.empty() : _set.big.empty(); - } - - size_t size() const { - return is_small ? _set.small.size() : _set.big.size(); - } - - class const_iterator { - const bool is_small; - - union ItT { - SmallSetT::const_iterator small_it; - BigSetT::const_iterator big_it; - ItT() {} - ~ItT() {} - } _it; - - public: - const_iterator(const SmallNumberSet& S, bool end = false) - : is_small(S.is_small) { - if (is_small) { - if (end) - _it.small_it = S._set.small.end(); - else - _it.small_it = S._set.small.begin(); - } else { - if (end) - _it.big_it = S._set.big.end(); - else - _it.big_it = S._set.big.begin(); - } - } - - const_iterator(const const_iterator&) = default; - - const_iterator& operator++() { - if (is_small) - ++_it.small_it; - else - ++_it.big_it; - - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - size_t operator*() const { - if (is_small) - return *_it.small_it; - else - return *_it.big_it; - } - - bool operator==(const const_iterator& rhs) const { - if (is_small != rhs.is_small) - return false; - - if (is_small) - return _it.small_it == rhs._it.small_it; - else - return _it.big_it == rhs._it.big_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - }; - - const_iterator begin() const { return const_iterator(*this); } - const_iterator end() const { return const_iterator(*this, true /* end */); } - - friend class const_iterator; -}; - -#if 0 -// This class is a container for a set of numbers -// that is optimized for holding small values -// (values less than sizeof(NumT)*8*SmallElemNum)). -// If a greater value is inserted, the whole container -// is lifted to a normal set. -// There is no possibility to remove elements from the set. -template -class SmallNumberSet { - NumT[SmallElemNum] _smallNums{}; - std::unique_ptr _bigSet; - - static size_t smallSetElemSize() { return sizeof(NumT)*8; } - static size_t smallSetSize() { return smallSetElemSize*SmallElemNum; } - static bool isInSmallSet(NumT n) { return n < smallSetSize(); } - - // split a number from small set to offset to _smallNums - // and a bit of the element - std::pair _split(NumT n) { - assert(!_bigSet); - - size_t pos = 0; - while (n >= smallSetElemSize()) { - n =>> smallSetElemSize() - 1; - ++pos; - } - - assert(pos < SmallElemNum); - return {pos, n}; - } - - // add a number - bool _add(NumT n) { - if (isInSmallSet(n)) { - size_t pos; - std::tie(pos, n) = _getSmallPos(n); - if (_smallNums[pos] & (1UL << num)) { - return false; - } else { - _smallNums[pos] |= (1UL << num); - return true; - } - } else { - assert(!_bigSet); - auto S = std::unique_ptr(new BigSetT(n)); - // copy the elements from small set to the big set - for (auto x : *this) - S.add(x); - - // set the big set - _bigSet = std::move(S); - - return true; - } - }; - - size_t _sizeSmall() const { - assert(!_bigSet); - size_t n; - for (auto x : _smallNums) { - while(x) { - if (x & 0x1) - ++n; - x >>= 1; - } - } - - return n; - } - - bool _emptySmall() const { return _sizeSmall() == 0; } - - bool _hasSmall(NumT n) const { - std::tie(pos, n) = _getSmallPos(n); - assert(pos < smallSetSize()); - assert(n < smallSetElemSize()); - - auto pos = _getSmallPos(n); - return _smallNums[pos] & (1UL << num); - } - -public: - bool add(NumT n) { return _bigSet ? _bigSet->add(n) : _add(n); } - bool has(NumT n) const { return _bigSet ? _bigSet.has(n) : _hasSmall(n); } - bool empty() const { return _bigSet ? _bigSet.empty() : _emptySmall(); } - size_t size() const { return _bigSet ? _bigSet.size() : _sizeSmall(); } - - /* - const_iterator begin() const { return _bitvector.begin(); } - const_iterator end() const { return _bitvector.end(); } - */ -} -#endif - -/* -// this is just a wrapper around std::set -template -class SimpleNumberSet { - using ContainerT = std::set; -} -*/ - -} // namespace ADT -} // namespace dg - -#endif // _DG_NUMBER_SET_H_ diff --git a/include/dg/ADT/Queue.h b/include/dg/ADT/Queue.h deleted file mode 100644 index aad3b463b..000000000 --- a/include/dg/ADT/Queue.h +++ /dev/null @@ -1,138 +0,0 @@ -#ifndef _DG_ADT_QUEUE_H_ -#define _DG_ADT_QUEUE_H_ - -#include -#include -#include - -namespace dg { -namespace ADT { - -template -class QueueLIFO -{ - using ContainerT = std::stack; - -public: - using ValueType = ValueT; - - ValueT pop() - { - ValueT ret = Container.top(); - Container.pop(); - - return ret; - } - - ValueT& top() - { - return Container.top(); - } - - void push(const ValueT& what) - { - Container.push(what); - } - - bool empty() const - { - return Container.empty(); - } - - typename ContainerT::size_type size() const - { - return Container.size(); - } - - void swap(QueueLIFO& oth) - { - Container.swap(oth.Container); - } - -private: - ContainerT Container; -}; - -template -class QueueFIFO -{ - using ContainerT = std::queue; - -public: - using ValueType = ValueT; - - ValueT pop() - { - ValueT ret = Container.front(); - Container.pop(); - - return ret; - } - - ValueT& top() - { - return Container.top(); - } - - void push(const ValueT& what) - { - Container.push(what); - } - - bool empty() const - { - return Container.empty(); - } - - typename ContainerT::size_type size() const - { - return Container.size(); - } - - void swap(QueueFIFO& oth) - { - Container.swap(oth.Container); - } - -private: - ContainerT Container; -}; - -template -class PrioritySet -{ - using ContainerT = std::set; -public: - using ValueType = ValueT; - - ValueT pop() - { - ValueT ret = *(Container.begin()); - Container.erase(Container.begin()); - - return ret; - } - - void push(const ValueT& what) - { - Container.insert(what); - } - - bool empty() const - { - return Container.empty(); - } - - typename ContainerT::size_type size() const - { - return Container.size(); - } - -private: - ContainerT Container; -}; - -} // namespace ADT -} // namespace dg - -#endif // _DG_ADT_QUEUE_H_ diff --git a/include/dg/ADT/STLHashMap.h b/include/dg/ADT/STLHashMap.h deleted file mode 100644 index fc1702a01..000000000 --- a/include/dg/ADT/STLHashMap.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef DG_STL_HASH_MAP_H_ -#define DG_STL_HASH_MAP_H_ - -#include - -#include "HashMapImpl.h" - -namespace dg { - -template -class STLHashMap : public HashMapImpl> {}; - - -// unordered_map that caches last several accesses -// XXX: use a different implementation than std::unordered_map -template -class CachingHashMap : public std::unordered_map { - std::pair _cache[CACHE_SIZE]; - unsigned _insert_pos{0}; - unsigned _last{0}; - - T *_get_from_cache(const Key& key) { - if (_last > 0) { - for (unsigned i = 0; i < _last; ++i) { - if (_cache[i].first == key) - return _cache[i].second; - } - } - return nullptr; - } - - void _insert_to_cache(const Key& key, T *v) { - _last = std::min(_last + 1, CACHE_SIZE); - _cache[_insert_pos] = {key, v}; - _insert_pos = (_insert_pos + 1) % CACHE_SIZE; - - assert(_insert_pos < CACHE_SIZE); - assert(_last <= CACHE_SIZE); - } - - void _invalidate_cache() { - _last = 0; - _insert_pos = 0; - } - -public: - using iterator = typename std::unordered_map::iterator; - using const_iterator = typename std::unordered_map::const_iterator; - - T& operator[](const Key& key) { - if (auto *v = _get_from_cache(key)) { - return *v; - } - - auto& ret = std::unordered_map::operator[](key); - _insert_to_cache(key, &ret); - return ret; - } - - iterator erase(const_iterator pos) { - _invalidate_cache(); - return std::unordered_map::erase(pos); - } - - iterator erase(const_iterator first, const_iterator last) { - _invalidate_cache(); - return std::unordered_map::erase(first, last); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/ADT/SetQueue.h b/include/dg/ADT/SetQueue.h deleted file mode 100644 index 24f50b454..000000000 --- a/include/dg/ADT/SetQueue.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef DG_ADT_SET_QUEUE_H_ -#define DG_ADT_SET_QUEUE_H_ - -#include -#include "Queue.h" - -namespace dg { -namespace ADT { - -// A queue where each element can be queued only once -template -class SetQueue { - std::set _queued; - QueueT _queue; - -public: - using ValueType = typename QueueT::ValueType; - - ValueType pop() { return _queue.pop(); } - ValueType& top() { return _queue.top(); } - bool empty() const { return _queue.empty(); } - size_t size() const { return _queue.size(); } - - void push(const ValueType& what) { - if (_queued.insert(what).second) - _queue.push(what); - } - - void swap(SetQueue& oth) { - _queue.swap(oth._queue); - _queued.swap(oth._queued); - } -}; - -} // namespace ADT -} // namespace dg - -#endif // DG_ADT_QUEUE_H_ diff --git a/include/dg/ADT/TslHopscotchHashMap.h b/include/dg/ADT/TslHopscotchHashMap.h deleted file mode 100644 index 9d39ce345..000000000 --- a/include/dg/ADT/TslHopscotchHashMap.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef DG_TSL_HOPSCOTCH_MAP_H_ -#define DG_TSL_HOPSCOTCH_MAP_H_ - -#include - -#include "HashMapImpl.h" - -namespace dg { - -template -class HopscotchHashMap : public HashMapImpl> { -}; - -} // namespace dg - -#endif diff --git a/include/dg/AnalysisOptions.h b/include/dg/AnalysisOptions.h deleted file mode 100644 index 0b8f7dc47..000000000 --- a/include/dg/AnalysisOptions.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef DG_ANALYSIS_OPTIONS_H_ -#define DG_ANALYSIS_OPTIONS_H_ - -#include "Offset.h" -#include -#include - -namespace dg { - -/// -// Enumeration for functions that are known to -// return freshly allocated memory. -enum class AllocationFunction { - NONE, // not an allocation function - MALLOC, // function behaves like malloc - CALLOC, // function behaves like calloc - ALLOCA, // function behaves like alloca - REALLOC, // function behaves like realloc - MALLOC0, // function behaves like malloc, - // but cannot return NULL - CALLOC0, // function behaves like calloc, - // but cannot return NULL -}; - -struct AnalysisOptions { - // Number of bytes in objects to track precisely - Offset fieldSensitivity{Offset::UNKNOWN}; - - AnalysisOptions& setFieldSensitivity(Offset o) { - fieldSensitivity = o; return *this; - } - - std::map allocationFunctions = { - {"malloc", AllocationFunction::MALLOC}, - {"calloc", AllocationFunction::CALLOC}, - {"alloca", AllocationFunction::ALLOCA}, - {"realloc", AllocationFunction::REALLOC}, - }; - - void addAllocationFunction(const std::string& name, AllocationFunction F) { -#ifndef NDEBUG - auto ret = -#endif - allocationFunctions.emplace(name, F); - assert(ret.second && "Already have this allocation function"); - } - - AllocationFunction getAllocationFunction(const std::string& name) const { - auto it = allocationFunctions.find(name); - if (it == allocationFunctions.end()) - return AllocationFunction::NONE; - return it->second; - } - - bool isAllocationFunction(const std::string& name) const { - return getAllocationFunction(name) != AllocationFunction::NONE; - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/BBlock.h b/include/dg/BBlock.h deleted file mode 100644 index 380df0c37..000000000 --- a/include/dg/BBlock.h +++ /dev/null @@ -1,506 +0,0 @@ -#ifndef BBLOCK_H_ -#define BBLOCK_H_ - -#include -#include - -#include "ADT/DGContainer.h" -#include "legacy/Analysis.h" - -namespace dg { - -/// ------------------------------------------------------------------ -// - BBlock -// Basic block structure for dependence graph -/// ------------------------------------------------------------------ -template -class BBlock -{ -public: - using KeyT = typename NodeT::KeyType; - using DependenceGraphT = typename NodeT::DependenceGraphType; - - struct BBlockEdge { - BBlockEdge(BBlock* t, uint8_t label = 0) - : target(t), label(label) {} - - BBlock *target; - // we'll have just numbers as labels now. - // We can change it if there's a need - uint8_t label; - - bool operator==(const BBlockEdge& oth) const - { - return target == oth.target && label == oth.label; - } - - bool operator!=(const BBlockEdge& oth) const - { - return operator==(oth); - } - - bool operator<(const BBlockEdge& oth) const - { - return target == oth.target ? - label < oth.label : target < oth.target; - } - }; - - BBlock(NodeT *head = nullptr, DependenceGraphT *dg = nullptr) - : key(KeyT()), dg(dg), ipostdom(nullptr), slice_id(0) - { - if (head) { - append(head); - assert(!dg || head->getDG() == nullptr || dg == head->getDG()); - } - } - - ~BBlock() { - if (delete_nodes_on_destr) { - for (NodeT *nd : nodes) - delete nd; - } - } - - using BBlockContainerT = EdgesContainer>; - // we don't need labels with predecessors - using PredContainerT = EdgesContainer>; - using SuccContainerT = DGContainer; - - SuccContainerT& successors() { return nextBBs; } - const SuccContainerT& successors() const { return nextBBs; } - - PredContainerT& predecessors() { return prevBBs; } - const PredContainerT& predecessors() const { return prevBBs; } - - const BBlockContainerT& controlDependence() const { return controlDeps; } - const BBlockContainerT& revControlDependence() const { return revControlDeps; } - - // similary to nodes, basic blocks can have keys - // they are not stored anywhere, it is more due to debugging - void setKey(const KeyT& k) { key = k; } - const KeyT& getKey() const { return key; } - - // XXX we should do it a common base with node - // to not duplicate this - something like - // GraphElement that would contain these attributes - void setDG(DependenceGraphT *d) { dg = d; } - DependenceGraphT *getDG() const { return dg; } - - const std::list& getNodes() const { return nodes; } - std::list& getNodes() { return nodes; } - bool empty() const { return nodes.empty(); } - size_t size() const { return nodes.size(); } - - void append(NodeT *n) - { - assert(n && "Cannot add null node to BBlock"); - - n->setBasicBlock(this); - nodes.push_back(n); - } - - void prepend(NodeT *n) - { - assert(n && "Cannot add null node to BBlock"); - - n->setBasicBlock(this); - nodes.push_front(n); - } - - bool hasControlDependence() const - { - return !controlDeps.empty(); - } - - // return true if all successors point - // to the same basic block (not considering labels, - // just the targets) - bool successorsAreSame() const - { - if (nextBBs.size() < 2) - return true; - - typename SuccContainerT::const_iterator start, iter, end; - iter = nextBBs.begin(); - end = nextBBs.end(); - - BBlock *block = iter->target; - // iterate over all successor and - // check if they are all the same - for (++iter; iter != end; ++iter) - if (iter->target != block) - return false; - - return true; - } - - // return true if all successors point - // to the same basic block (not considering labels, - // just the targets) - bool hasSuccessor(BBlock *B) const - { - for (auto& succB : successors()) { - if (succB.target == B) { - return true; - } - } - - return false; - } - - // remove all edges from/to this BB and reconnect them to - // other nodes - void isolate() - { - // take every predecessor and reconnect edges from it - // to successors - for (BBlock *pred : prevBBs) { - // find the edge that is going to this node - // and create new edges to all successors. The new edges - // will have the same label as the found one - DGContainer new_edges; - for (auto I = pred->nextBBs.begin(),E = pred->nextBBs.end(); I != E;) { - auto cur = I++; - if (cur->target == this) { - // create edges that will go from the predecessor - // to every successor of this node - for (const BBlockEdge& succ : nextBBs) { - // we cannot create an edge to this bblock (we're isolating _this_ bblock), - // that would be incorrect. It can occur when we're isolatin a bblock - // with self-loop - if (succ.target != this) - new_edges.insert(BBlockEdge(succ.target, cur->label)); - } - - // remove the edge from predecessor - pred->nextBBs.erase(*cur); - } - } - - // add newly created edges to predecessor - for (const BBlockEdge& edge : new_edges) { - assert(edge.target != this - && "Adding an edge to a block that is being isolated"); - pred->addSuccessor(edge); - } - } - - removeSuccessors(); - - // NOTE: nextBBs were cleared in removeSuccessors() - prevBBs.clear(); - - // remove reverse edges to this BB - for (BBlock *B : controlDeps) { - // we don't want to corrupt the iterator - // if this block is control dependent on itself. - // We're gonna clear it anyway - if (B == this) - continue; - - B->revControlDeps.erase(this); - } - - // clear also cd edges that blocks have - // to this block - for (BBlock *B : revControlDeps) { - if (B == this) - continue; - - B->controlDeps.erase(this); - } - - revControlDeps.clear(); - controlDeps.clear(); - } - - void remove(bool with_nodes = true) - { - // do not leave any dangling reference - isolate(); - - if (dg) { -#ifndef NDEBUG - bool ret = -#endif - dg->removeBlock(key); - assert(ret && "BUG: block was not in DG"); - if (dg->getEntryBB() == this) - dg->setEntryBB(nullptr); - } - - if (with_nodes) { - for (NodeT *n : nodes) { - // we must set basic block to nullptr - // otherwise the node will try to remove the - // basic block again if it is of size 1 - n->setBasicBlock(nullptr); - - // remove dependency edges, let be CFG edges - // as we'll destroy all the nodes - n->removeCDs(); - n->removeDDs(); - // remove the node from dg - assert(n->getDG()); - n->getDG()->removeNode(n); - - delete n; - } - } - - delete this; - } - - void removeNode(NodeT *n) { nodes.remove(n); } - - size_t successorsNum() const { return nextBBs.size(); } - size_t predecessorsNum() const { return prevBBs.size(); } - - bool addSuccessor(const BBlockEdge& edge) - { - bool ret = nextBBs.insert(edge); - edge.target->prevBBs.insert(this); - - return ret; - } - - bool addSuccessor(BBlock *b, uint8_t label = 0) - { - return addSuccessor(BBlockEdge(b, label)); - } - - void removeSuccessors() - { - // remove references to this node from successors - for (const BBlockEdge& succ : nextBBs) { - // This assertion does not hold anymore, since if we have - // two edges with different labels to the same successor, - // and we remove the successor, then we remove 'this' - // from prevBBs twice. If we'll add labels even to predecessors, - // this assertion must hold again - // bool ret = succ.target->prevBBs.erase(this); - // assert(ret && "Did not have this BB in successor's pred"); - succ.target->prevBBs.erase(this); - } - - nextBBs.clear(); - } - - bool hasSelfLoop() - { - return nextBBs.contains(this); - } - - void removeSuccessor(const BBlockEdge& succ) - { - succ.target->prevBBs.erase(this); - nextBBs.erase(succ); - } - - unsigned removeSuccessorsTarget(BBlock *target) - { - unsigned removed = 0; - SuccContainerT tmp; - // approx - for (auto& edge : nextBBs) { - if (edge.target != target) - tmp.insert(edge); - else - ++removed; - } - - nextBBs.swap(tmp); - return removed; - } - - void removePredecessors() - { - for (BBlock *BB : prevBBs) - BB->nextBBs.erase(this); - - prevBBs.clear(); - } - - bool addControlDependence(BBlock *b) - { - bool ret; -#ifndef NDEBUG - bool ret2; -#endif - - ret = controlDeps.insert(b); -#ifndef NDEBUG - ret2 = -#endif - b->revControlDeps.insert(this); - - // we either have both edges or none - assert(ret == ret2); - - return ret; - } - - // get first node from bblock - // or nullptr if the block is empty - NodeT *getFirstNode() const - { - if (nodes.empty()) - return nullptr; - - return nodes.front(); - } - - // get last node from block - // or nullptr if the block is empty - NodeT *getLastNode() const - { - if (nodes.empty()) - return nullptr; - - return nodes.back(); - } - - // XXX: do this optional? - BBlockContainerT& getPostDomFrontiers() { return postDomFrontiers; } - const BBlockContainerT& getPostDomFrontiers() const { return postDomFrontiers; } - - bool addPostDomFrontier(BBlock *BB) - { - return postDomFrontiers.insert(BB); - } - - bool addDomFrontier(BBlock *DF) - { - return domFrontiers.insert(DF); - } - - BBlockContainerT& getDomFrontiers() { return domFrontiers; } - const BBlockContainerT& getDomFrontiers() const { return domFrontiers; } - - void setIPostDom(BBlock *BB) - { - assert(!ipostdom && "Already has the immedate post-dominator"); - ipostdom = BB; - BB->postDominators.insert(this); - } - - BBlock *getIPostDom() { return ipostdom; } - const BBlock *getIPostDom() const { return ipostdom; } - - BBlockContainerT& getPostDominators() { return postDominators; } - const BBlockContainerT& getPostDominators() const { return postDominators; } - - void setIDom(BBlock* BB) - { - assert(!idom && "Already has immediate dominator"); - idom = BB; - BB->addDominator(this); - } - - void addDominator(BBlock* BB) - { - assert( BB && "need dominator bblock" ); - dominators.insert(BB); - } - - BBlock *getIDom() { return idom; } - const BBlock *getIDom() const { return idom; } - - BBlockContainerT& getDominators() { return dominators; } - const BBlockContainerT& getDominators() const { return dominators; } - - unsigned int getDFSOrder() const - { - return analysisAuxData.dfsorder; - } - - // in order to fasten up interprocedural analyses, - // we register all the call sites in the BBlock - unsigned int getCallSitesNum() const - { - return callSites.size(); - } - - const std::set& getCallSites() - { - return callSites; - } - - bool addCallsite(NodeT *n) - { - assert(n->getBBlock() == this - && "Cannot add callsite from different BB"); - - return callSites.insert(n).second; - } - - bool removeCallSite(NodeT *n) - { - assert(n->getBBlock() == this - && "Removing callsite from different BB"); - - return callSites.erase(n) != 0; - } - - void setSlice(uint64_t sid) - { - slice_id = sid; - } - - uint64_t getSlice() const { return slice_id; } - - void deleteNodesOnDestruction(bool v = true) { - delete_nodes_on_destr = v; - } - -private: - // optional key - KeyT key; - - // reference to dg if needed - DependenceGraphT *dg; - - // nodes contained in this bblock - std::list nodes; - - SuccContainerT nextBBs; - PredContainerT prevBBs; - - // when we have basic blocks, we do not need - // to keep control dependencies in nodes, because - // all nodes in block has the same control dependence - BBlockContainerT controlDeps; - BBlockContainerT revControlDeps; - - // post-dominator frontiers - BBlockContainerT postDomFrontiers; - BBlock *ipostdom; - // the post-dominator tree edges - // (reverse to immediate post-dominator) - BBlockContainerT postDominators; - - // parent of @this in dominator tree - BBlock *idom = nullptr; - // BB.dominators = all children in dominator tree - BBlockContainerT dominators; - // dominance frontiers - BBlockContainerT domFrontiers; - - // is this block in some slice? - uint64_t slice_id; - - // delete nodes on destruction of the block - bool delete_nodes_on_destr = false; - - // auxiliary data for analyses - std::set callSites; - - // auxiliary data for different analyses - legacy::AnalysesAuxiliaryData analysisAuxData; - friend class legacy::BBlockAnalysis; -}; - -} // namespace dg - -#endif // _BBLOCK_H_ diff --git a/include/dg/BBlockBase.h b/include/dg/BBlockBase.h deleted file mode 100644 index 63da4278b..000000000 --- a/include/dg/BBlockBase.h +++ /dev/null @@ -1,109 +0,0 @@ -#ifndef DG_BBLOCK_BASE_H_ -#define DG_BBLOCK_BASE_H_ - -#include -#include - -namespace dg { - -class ElemId { - static unsigned idcnt; - unsigned id; -public: - ElemId() : id(++idcnt) {} - unsigned getID() const { return id; } -}; - -template -class ElemWithEdges { - using EdgesT = std::vector; - -protected: - EdgesT _successors; - EdgesT _predecessors; - -public: - auto succ_begin() -> decltype(_successors.begin()) { return _successors.begin(); } - auto succ_end() -> decltype(_successors.begin()) { return _successors.end(); } - auto pred_begin() -> decltype(_predecessors.begin()) { return _predecessors.begin(); } - auto pred_end() -> decltype(_predecessors.begin()) { return _predecessors.end(); } - auto succ_begin() const -> decltype(_successors.begin()) { return _successors.begin(); } - auto succ_end() const -> decltype(_successors.begin()) { return _successors.end(); } - auto pred_begin() const -> decltype(_predecessors.begin()) { return _predecessors.begin(); } - auto pred_end() const -> decltype(_predecessors.begin()) { return _predecessors.end(); } - - const EdgesT successors() const { return _successors; } - const EdgesT predecessors() const { return _predecessors; } - - bool hasSuccessors() const { return !_successors.empty(); } - bool hasPredecessors() const { return !_predecessors.empty(); } - - void addSuccessor(ElemT *s) { - for (auto *succ : _successors) { - if (succ == s) - return; - } - - _successors.push_back(s); - - for (auto *pred : s->_predecessors) { - if (pred == this) - return; - } - s->_predecessors.push_back(static_cast(this)); - } - - ElemT *getSinglePredecessor() { - return _predecessors.size() == 1 ? _predecessors.back() : nullptr; - } - - ElemT *getSingleSuccessor() { - return _successors.size() == 1 ? _successors.back() : nullptr; - } -}; - -template -class CFGElement : public ElemId, public ElemWithEdges { }; - -template -class BBlockBase : public CFGElement { - using NodesT = std::list; - - NodesT _nodes; - -public: - - void append(NodeT *n) { _nodes.push_back(n); n->setBBlock(static_cast(this)); } - void prepend(NodeT *n) { _nodes.push_front(n); n->setBBlock(static_cast(this)); } - - void insertBefore(NodeT *n, NodeT *before) { - assert(!_nodes.empty()); - - auto it = _nodes.begin(); - while (it != _nodes.end()) { - if (*it == before) - break; - ++it; - } - assert(it != _nodes.end() && "Did not find 'before' node"); - - _nodes.insert(it, n); - n->setBBlock(static_cast(this)); - } - - // FIXME: rename to nodes() - const NodesT& getNodes() const { return _nodes; } - NodesT& getNodes() { return _nodes; } - // FIXME: rename to first/front(), last/back() - NodeT *getFirst() { return _nodes.empty() ? nullptr : _nodes.front(); } - NodeT *getLast() { return _nodes.empty() ? nullptr : _nodes.back(); } - const NodeT *getFirst() const { return _nodes.empty() ? nullptr : _nodes.front(); } - const NodeT *getLast() const { return _nodes.empty() ? nullptr : _nodes.back(); } - - bool empty() const { return _nodes.empty(); } - auto size() const -> decltype(_nodes.size()) { return _nodes.size(); } -}; - -} - -#endif diff --git a/include/dg/BBlocksBuilder.h b/include/dg/BBlocksBuilder.h deleted file mode 100644 index e7fd8df97..000000000 --- a/include/dg/BBlocksBuilder.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef DG_BBLOCKS_BUILDER_H_ -#define DG_BBLOCKS_BUILDER_H_ - -#include -#include - -#include "dg/ADT/Queue.h" - -namespace dg { - -/// -// Generate basic blocks from nodes with successors. -template -class BBlocksBuilder { - using NodeT = typename BBlockT::NodeT; - - std::vector> _blocks; - - // FIXME: use bitvector? - std::set _processed; - ADT::QueueFIFO _queue; - - bool enqueue(NodeT *n) { - //the id 0 is reserved for invalid nodes - assert(n->getID() != 0 && "Queued invalid node"); - - if (_processed.insert(n->getID()).second == false) - return false; // we already queued this node - - _queue.push(n); - assert(enqueue(n) == false); - return true; - } - - void setNewBlock(NodeT *cur) { - auto blk = new BBlockT(); - _blocks.emplace_back(blk); - blk->append(cur); - cur->setBBlock(blk); - } - - void addToBlock(NodeT *cur, BBlockT *blk) { - cur->setBBlock(blk); - blk->append(cur); - } - - void setBlock(NodeT *cur) { - if (cur->predecessorsNum() == 0 // root node - || cur->predecessorsNum() > 1) { // join - setNewBlock(cur); - return; - } - - assert(cur->predecessorsNum() == 1); - // if we are the entry node after branching, - // we create a new block - if (cur->getSinglePredecessor()->successorsNum() > 1) { - setNewBlock(cur); - return; - } - - // We are inside a block, set the block from - // the predecessor - assert(cur->getSinglePredecessor()->getBBlock()); - addToBlock(cur, cur->getSinglePredecessor()->getBBlock()); - } - -public: - void buildBlocks(NodeT *root) { - enqueue(root); - - while (!_queue.empty()) { - NodeT *cur = _queue.pop(); - assert(cur->getBBlock() == nullptr); - - setBlock(cur); - - // queue successors for processing - for (NodeT *succ : cur->successors()) { - enqueue(succ); - } - } - } - - std::vector>& getBlocks() { return _blocks; } - - std::vector>&& - buildAndGetBlocks(NodeT *root) { - buildBlocks(root); - return std::move(_blocks); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/BFS.h b/include/dg/BFS.h deleted file mode 100644 index 80c8ec46e..000000000 --- a/include/dg/BFS.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DG_BFS_H_ -#define DG_BFS_H_ - -#include "dg/NodesWalk.h" -#include "dg/ADT/Queue.h" - -using dg::ADT::QueueFIFO; - -namespace dg { - -template , - typename EdgeChooser = SuccessorsEdgeChooser > -struct BFS : public NodesWalk, VisitTracker, EdgeChooser> { - BFS() = default; - BFS(EdgeChooser chooser) : NodesWalk, VisitTracker, EdgeChooser>(std::move(chooser)) {} - BFS(VisitTracker tracker) : NodesWalk, VisitTracker, EdgeChooser>(std::move(tracker)) {} - BFS(VisitTracker tracker, EdgeChooser chooser) - : NodesWalk, VisitTracker, EdgeChooser>(std::move(tracker), std::move(chooser)) {} -}; - -} // namespace dg - -#endif diff --git a/include/dg/CallGraph/CallGraph.h b/include/dg/CallGraph/CallGraph.h deleted file mode 100644 index 3d7e64209..000000000 --- a/include/dg/CallGraph/CallGraph.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef DG_GENERIC_CALLGRAPH_H_ -#define DG_GENERIC_CALLGRAPH_H_ - -#include -#include - -namespace dg { - -template -class GenericCallGraph { -public: - class FuncNode { - unsigned _id; - unsigned _scc_id{0}; - std::vector _calls; - std::vector _callers; - - template - bool _contains(const FuncNode *x, const Cont& C) const { - for (auto s : C) { - if (s == x) - return true; - } - return false; - } - - public: - const ValueT value; - - FuncNode(unsigned id, const ValueT& nd) : _id(id), value(nd) {}; - FuncNode(FuncNode&&) = default; - - bool calls(const FuncNode *x) const { return _contains(x, _calls); } - bool isCalledBy(FuncNode *x) const { return _contains(x, _callers); } - - unsigned getID() const { return _id; } - unsigned getSCCId() const { return _scc_id; } - void setSCCId(unsigned id) { _scc_id = id; } - - - bool addCall(FuncNode *x) { - if (calls(x)) - return false; - _calls.push_back(x); - if (!x->isCalledBy(this)) - x->_callers.push_back(this); - return true; - } - - const std::vector& getCalls() const { return _calls; } - // alias for getCalls() - const std::vector& successors() const { return getCalls(); } - const std::vector& getCallers() const { return _callers; } - - const ValueT& getValue() const { return value; }; - }; - -private: - unsigned last_id{0}; - - FuncNode *getOrCreate(const ValueT& v) { - auto it = _mapping.find(v); - if (it == _mapping.end()) { - auto newIt = _mapping.emplace(v, FuncNode(++last_id, v)); - return &newIt.first->second; - } - return &it->second; - } - - std::map _mapping; - -public: - - // just create a node for the value - // (e.g., the entry node) - FuncNode *createNode(const ValueT& a) { - return getOrCreate(a); - } - - // a calls b - bool addCall(const ValueT& a, const ValueT& b) { - auto A = getOrCreate(a); - auto B = getOrCreate(b); - return A->addCall(B); - } - - const FuncNode *get(const ValueT& v) const { - auto it = _mapping.find(v); - if (it == _mapping.end()) { - return nullptr; - } - return &it->second; - } - - FuncNode *get(const ValueT& v) { - auto it = _mapping.find(v); - if (it == _mapping.end()) { - return nullptr; - } - return &it->second; - } - - bool empty() const { return _mapping.empty(); } - - auto begin() -> decltype(_mapping.begin()) { return _mapping.begin(); } - auto end() -> decltype(_mapping.end()) { return _mapping.end(); } - auto begin() const -> decltype(_mapping.begin()) { return _mapping.begin(); } - auto end() const -> decltype(_mapping.end()) { return _mapping.end(); } -}; - -} // namespace dg - -#endif diff --git a/include/dg/ControlDependence/CDGraph.h b/include/dg/ControlDependence/CDGraph.h deleted file mode 100644 index 8ecbc654b..000000000 --- a/include/dg/ControlDependence/CDGraph.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef DG_LLVM_CDGRAPH_H_ -#define DG_LLVM_CDGRAPH_H_ - -#include -#include -#include -#include - -#include "dg/BBlockBase.h" - -namespace dg { - -////////////////////////////////////////////////////////////////// -/// Basic graph elements from which all other elements inherit. -////////////////////////////////////////////////////////////////// - -///// -/// The basic class for the graph. It is either an instruction or a block, -/// depending on the chosen granularity of CFG. -///// -class CDNode; -class CDGraph; -class CDNode : public ElemWithEdges { - friend class CDGraph; - - unsigned _id; - CDNode(unsigned id) : _id(id) {} - -public: - unsigned getID() const { return _id; } -}; - -///// -/// CDGraph - a graph that is used for the computation of control dependencies. -/// It contains nodes that correspond either to basic blocks or instructions -/// along with the successor relation. -///// -class CDGraph { - using NodesVecT = std::vector>; - //using NodesPtrVecT = ADT::SparseBitvector; - // FIXME ^^ - using PredicatesT = std::set; - - std::string _name; - NodesVecT _nodes; - //NodesPtrVecT _predicates; - PredicatesT _predicates; - - // iterator over the subgraphs that unwraps the unique_ptr - struct node_iterator : public NodesVecT::iterator { - using ContainedType - = typename std::remove_reference()->get()))>::type; - - node_iterator(const typename NodesVecT::iterator& it) : NodesVecT::iterator(it) {} - node_iterator(const node_iterator&) = default; - node_iterator() = default; - - ContainedType *operator*() { - return (NodesVecT::iterator::operator*()).get(); - }; - ContainedType *operator->() { - return ((NodesVecT::iterator::operator*()).get()); - }; - }; - - struct nodes_range { - NodesVecT& nodes; - nodes_range(NodesVecT& b) : nodes(b) {} - - node_iterator begin() { return node_iterator(nodes.begin()); } - node_iterator end() { return node_iterator(nodes.end()); } - }; - - // iterator over the predicates - /* - struct predicate_iterator : public NodesPtrVecT::const_iterator { - NodesVecT& _nodes; - - predicate_iterator(NodesVecT& nodes, const typename NodesPtrVecT::const_iterator& it) - : NodesPtrVecT::const_iterator(it), _nodes(nodes) {} - predicate_iterator(const predicate_iterator&) = default; - - CDNode *operator*() { - auto id = NodesPtrVecT::const_iterator::operator*(); - assert(id - 1 < _nodes.size()); - return this->_nodes[id - 1].get(); - }; - CDNode *operator->() { - auto id = NodesPtrVecT::const_iterator::operator*(); - assert(id - 1 < _nodes.size()); - return this->_nodes[id - 1].get(); - }; - }; - - struct predicates_range { - NodesVecT& nodes; - NodesPtrVecT& predicates; - predicates_range(NodesVecT& n, NodesPtrVecT& b) : nodes(n), predicates(b) {} - - predicate_iterator begin() { return predicate_iterator(nodes, predicates.begin()); } - predicate_iterator end() { return predicate_iterator(nodes, predicates.end()); } - }; - */ - -public: - CDGraph(const std::string& name = "") : _name(name) {} - CDGraph(const CDGraph& rhs) = delete; - CDGraph& operator=(const CDGraph&) = delete; - CDGraph(CDGraph&&) = default; - CDGraph& operator=(CDGraph&&) = default; - - CDNode& createNode() { - auto *nd = new CDNode(_nodes.size() + 1); - _nodes.emplace_back(nd); - assert(_nodes.back()->getID() == _nodes.size() - && "BUG: we rely on the ordering by ids"); - return *nd; - } - - // add an edge between two nodes in the graph. - // This method registers also what nodes have more than successor - void addNodeSuccessor(CDNode& nd, CDNode& succ) { - nd.addSuccessor(&succ); - if (nd.successors().size() > 1) { - _predicates.insert(&nd); - } - } - - node_iterator begin() { return node_iterator(_nodes.begin()); } - node_iterator end() { return node_iterator(_nodes.end()); } - nodes_range nodes() { return nodes_range(_nodes); } - - CDNode *getNode(unsigned id) { - assert(id - 1 < _nodes.size()); - return _nodes[id - 1].get(); - } - - const CDNode *getNode(unsigned id) const { - assert(id - 1 < _nodes.size()); - return _nodes[id - 1].get(); - } - - size_t size() const { return _nodes.size(); } - bool empty() const { return _nodes.empty(); } - - /* - predicate_iterator predicates_begin() { return predicate_iterator(_nodes, _predicates.begin()); } - predicate_iterator predicates_end() { return predicate_iterator(_nodes, _predicates.end()); } - predicates_range predicates() { return predicates_range(_nodes, _predicates); } - */ - - decltype(_predicates.begin()) predicates_begin() { return _predicates.begin(); } - decltype(_predicates.end()) predicates_end() { return _predicates.end(); } - decltype(_predicates.begin()) predicates_begin() const { return _predicates.begin(); } - decltype(_predicates.end()) predicates_end() const { return _predicates.end(); } - PredicatesT& predicates() { return _predicates; } - const PredicatesT& predicates() const { return _predicates; } - - /* - bool isPredicate(const CDNode& nd) const { - return _predicates.get(nd.getID()); - } - */ - - bool isPredicate(const CDNode& nd) const { - return _predicates.count(const_cast(&nd)) > 0; - } - - const std::string& getName() const { return _name; } -}; - -} // namespace dg - -#endif diff --git a/include/dg/ControlDependence/ControlClosure.h b/include/dg/ControlDependence/ControlClosure.h deleted file mode 100644 index cc311d41c..000000000 --- a/include/dg/ControlDependence/ControlClosure.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef CD_CONTROL_CLOSURE_H_ -#define CD_CONTROL_CLOSURE_H_ - -#include -#include - -#include "CDGraph.h" - -#include "dg/ADT/SetQueue.h" -#include "dg/ADT/Queue.h" - -namespace dg { - -class StrongControlClosure { - - // this is basically the \Theta function from the paper - template - void foreachFirstReachable(const Nodes& nodes, - CDNode *from, - const FunT& fun) { - ADT::SetQueue> queue; - for (auto *s : from->successors()) { - queue.push(s); - } - - while (!queue.empty()) { - auto *cur = queue.pop(); - if (nodes.count(cur) > 0) { // the node is from Ap? - fun(cur); - } else { - for (auto *s : cur->successors()) { - queue.push(s); - } - } - } - } - - // this is the \Gamma function from the paper - // (a bit different implementation) - std::set - gamma(CDGraph& graph, const std::set& targets) { - struct Info { - unsigned colored{false}; - unsigned short counter; - }; - - std::unordered_map data; - data.reserve(graph.size()); - ADT::QueueLIFO queue; - - // initialize nodes - for (auto *nd : graph) { - auto &D = data[nd]; - D.colored = false; - D.counter = nd->successors().size(); - } - - // initialize the search - for (auto *target : targets) { - data[target].colored = true; - queue.push(target); - } - - // search! - while (!queue.empty()) { - auto *node = queue.pop(); - assert(data[node].colored && "A non-colored node in queue"); - - for (auto *pred : node->predecessors()) { - auto& D = data[pred]; - --D.counter; - if (D.counter == 0) { - D.colored = true; - queue.push(pred); - } - } - } - - std::set retval; - for (auto *n : graph) { - if (!data[n].colored) { - retval.insert(n); - } - } - return retval; - } - - std::set theta(const std::set& X, CDNode *n) { - std::set retval; - if (X.count(n) > 0) { - retval.insert(n); - return retval; - } - foreachFirstReachable(X, n, [&](CDNode *cur) { - retval.insert(cur); - }); - return retval; - } - - -public: - using ValVecT = std::vector; - - void closeSet(CDGraph& G, std::set& X) { - while (true) { - ADT::SetQueue> queue; - for (auto *n : X) { - for (auto *s : n->successors()) { - queue.push(s); - } - } - - CDNode *toadd = nullptr; - while (!queue.empty()) { - assert(toadd == nullptr); - auto *p = queue.pop(); - for (auto *r : p->successors()) { - assert(toadd == nullptr); - //DBG(cda, "Checking edge " << p->getID() << "->" << r->getID()); - // (a) - if (theta(X, r).size() != 1) - continue; - - // (b) - auto gam = gamma(G, X); - if (gam.count(r) > 0) - continue; - - // (c) - if (theta(X, p).size() < 2 && gam.count(p) == 0) - continue; - - // all conditions met, we got our edge - //DBG(cda, "Found edge " << p->getID() << "->" << r->getID()); - assert(toadd == nullptr); - toadd = p; - break; - } - - if (toadd) - break; - - for (auto *s : p->successors()) { - queue.push(s); - } - } - - if (toadd) { - //DBG(cda, "Adding " << toadd->getID() << " to closure"); - X.insert(toadd); - continue; - } else { - // no other edge to process - break; - } - } - } - - ValVecT getClosure(CDGraph& G, const std::set& nodes) { - auto X = nodes; - closeSet(G, X); - return {X.begin(), X.end()}; - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/ControlDependence/ControlDependence.h b/include/dg/ControlDependence/ControlDependence.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/include/dg/ControlDependence/ControlDependenceAnalysisOptions.h b/include/dg/ControlDependence/ControlDependenceAnalysisOptions.h deleted file mode 100644 index 737dbed6e..000000000 --- a/include/dg/ControlDependence/ControlDependenceAnalysisOptions.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef DG_CDA_OPTIONS_H_ -#define DG_CDA_OPTIONS_H_ - -#include "dg/AnalysisOptions.h" - -namespace dg { - -struct ControlDependenceAnalysisOptions : AnalysisOptions { - // FIXME: add options class for CD - enum class CDAlgorithm { - STANDARD, - NTSCD_LEGACY, - NTSCD2, - NTSCD_RANGANATH, // fixed version of Ranganath's alg. - NTSCD_RANGANATH_ORIG, // original (wrong) version of Ranaganath's alg. - NTSCD, - DOD_RANGANATH, - DOD, - DODNTSCD, // DOD + NTSCD - STRONG_CC - } algorithm; - - // take into account interprocedural control dependencies - // (raising e.g., from calls to exit() which terminates the program) - bool interprocedural{true}; - - bool standardCD() const { return algorithm == CDAlgorithm::STANDARD; } - bool ntscdCD() const { return algorithm == CDAlgorithm::NTSCD; } - bool ntscd2CD() const { return algorithm == CDAlgorithm::NTSCD2; } - bool ntscdRanganathCD() const { return algorithm == CDAlgorithm::NTSCD_RANGANATH; } - bool ntscdRanganathOrigCD() const { return algorithm == CDAlgorithm::NTSCD_RANGANATH_ORIG; } - bool ntscdLegacyCD() const { return algorithm == CDAlgorithm::NTSCD_LEGACY; } - bool dodRanganathCD() const { return algorithm == CDAlgorithm::DOD_RANGANATH; } - bool dodCD() const { return algorithm == CDAlgorithm::DOD; } - bool dodntscdCD() const { return algorithm == CDAlgorithm::DODNTSCD; } - bool strongCC() const { return algorithm == CDAlgorithm::STRONG_CC; } - bool interproceduralCD() const { return interprocedural; } - - /// - // Return true if the computed control dependencies - // contain NTSCD dependencies - bool isNonterminationSensitive() const { - // DOD is for infinite loops, but it is not what we - // want when asking for non-termination sensitive... - return !standardCD() && !dodCD() && !dodRanganathCD(); - } -}; - -} // namespace dg -#endif diff --git a/include/dg/ControlDependence/DOD.h b/include/dg/ControlDependence/DOD.h deleted file mode 100644 index cced9f754..000000000 --- a/include/dg/ControlDependence/DOD.h +++ /dev/null @@ -1,625 +0,0 @@ -#ifndef DG_DOD_H_ -#define DG_DOD_H_ - -#include -#include -#include -#include - -#include -#include -#include - -#include "CDGraph.h" - -namespace dg { - -// compute which nodes lie on all max paths from a given node -// (for all nodes). It is basically the same as NTSCD class -// but we remember the results of calls to compute() -class AllMaxPath { -public: - using ResultT = std::map; - -private: - struct Info { - ADT::SparseBitvector colors; - unsigned short counter; - }; - - std::unordered_map data; - - void compute(CDGraph& graph, CDNode *target) { - - // initialize nodes - for (auto *nd : graph) { - data[nd].counter = nd->successors().size(); - } - - // initialize the search - data[target].colors.set(target->getID()); - ADT::QueueLIFO queue; - queue.push(target); - - // search! - while (!queue.empty()) { - auto *node = queue.pop(); - assert(data[node].colors.get(target->getID()) && "A non-colored node in queue"); - - for (auto *pred : node->predecessors()) { - auto& D = data[pred]; - --D.counter; - if (D.counter == 0) { - D.colors.set(target->getID()); - queue.push(pred); - } - } - } - } - -public: - - // returns mapping CDNode -> Set of CDNodes (where the set is implemented as a bitvector) - ResultT compute(CDGraph& graph) { - ResultT res; - - data.reserve(graph.size()); - - for (auto *nd : graph) { - compute(graph, nd); - res.emplace(nd, data[nd].colors); - } - - return res; - } -}; - - -class DOD { -public: - //using ResultT = std::map>>; - // NOTE: although DOD is a ternary relation, we treat it as binary - // by breaking a->(b, c) to (a, b) and (a, c). It has no effect - // on the results of slicing. - using ResultT = std::map>; - using ColoringT = ADT::SparseBitvector; - -private: - struct ColoredAp { - CDGraph Ap; - ColoringT blues; - ColoringT reds; - - // mapping G -> Ap - std::unordered_map _mapping{}; - // mapping Ap -> G - std::unordered_map _rev_mapping{}; - - CDNode *createNode(CDNode *gnode) { - auto *nd = &Ap.createNode(); - _mapping[gnode] = nd; - _rev_mapping[nd] = gnode; - return nd; - } - - CDNode *getNode(CDNode *gnode) { - auto it = _mapping.find(gnode); - return it == _mapping.end() ? nullptr : it->second; - } - - CDNode *getGNode(CDNode *apnode) { - auto it = _rev_mapping.find(apnode); - return it == _rev_mapping.end() ? nullptr : it->second; - } - - ColoredAp() = default; - ColoredAp(CDGraph&& g, ColoringT&& b, ColoringT&& r) - : Ap(std::move(g)), blues(std::move(b)), reds(std::move(r)) {} - ColoredAp(ColoredAp&&) = default; - ColoredAp(const ColoredAp&) = delete; - - bool isBlue(CDNode *n) const { return blues.get(n->getID()); } - bool isRed(CDNode *n) const { return reds.get(n->getID()); } - }; - - template - void foreachFirstReachable(const Nodes& nodes, - CDNode *from, - const FunT& fun) { - // FIXME: this breaks the complexity (it uses std::set) - ADT::SetQueue> queue; - for (auto *s : from->successors()) { - queue.push(s); - } - - while (!queue.empty()) { - auto *cur = queue.pop(); - if (nodes.get(cur->getID())) { // the node is from Ap? - fun(cur); - } else { - for (auto *s : cur->successors()) { - queue.push(s); - } - } - } - } - - // Create the Ap graph (nodes and edges) - ColoredAp createAp(const ADT::SparseBitvector& nodes, CDGraph& graph, CDNode *node) { - ColoredAp CAp; - CDGraph& Ap = CAp.Ap; - - // create nodes of graph - for (auto *n : graph) { - if (nodes.get(n->getID())) { - CAp.createNode(n); - //DBG(cda, " - Ap has node " << n->getID() << " (which is " << nd->getID() << " in Ap)"); - } - } - - assert(CAp.getNode(node) != nullptr); - - if (CAp.Ap.size() < 3) { - return {}; // no DOD possible, bail out early - } - - // Add edges. FIXME: we can use a better implementation - for (auto *n : Ap) { - auto *gn = CAp.getGNode(n); - - foreachFirstReachable(nodes, gn, [&](CDNode *cur) { - auto *apn = CAp.getNode(cur); - assert(apn); - n->addSuccessor(apn); - //DBG(cda, " - Ap edge " << gn->getID() << " -> " << cur->getID()); - }); - } - - assert(CAp.getNode(node) && "node is not in Ap"); - if (CAp.getNode(node)->successors().size() < 2) { - return {}; // no DOD possible, skip the rest of building colored Ap - } - - return CAp; - } - - // create the Ap graph (calls createAp) and color the nodes in the Ap graph - ColoredAp createColoredAp(AllMaxPath::ResultT& allpaths, CDGraph& graph, CDNode *node) { - auto it = allpaths.find(node); - if (it == allpaths.end()) { - return {}; - } - const auto& nodes = it->second; - - ColoredAp CAp = createAp(nodes, graph, node); - if (CAp.Ap.empty()) { - return {}; - } - - // initialize the colors - assert(node->successors().size() == 2 - && "Node is not the right predicate"); - - // FIXME: refactor... - // color nodes - auto& succs = node->successors(); - auto sit = succs.begin(); - CDNode *bluesucc = *sit; - CDNode *redsucc = *(++sit); - //DBG(cda, "Blue successor: " << bluesucc->getID()); - //DBG(cda, "Red successor: " << redsucc->getID()); - assert(++sit == succs.end()); - - if (nodes.get(bluesucc->getID())) { // is blue successor in Ap? - auto *apn = CAp.getNode(bluesucc); - assert(apn); - CAp.blues.set(apn->getID()); - //DBG(cda, " - Ap blue: " << apn->getID() << " (" - // << bluesucc->getID() << " in original)"); - } else { - foreachFirstReachable(nodes, bluesucc, [&](CDNode *cur) { - auto *apn = CAp.getNode(cur); - assert(apn); - CAp.blues.set(apn->getID()); - //DBG(cda, " - Ap blue: " << apn->getID() << " (" - // << cur->getID() << " in original)"); - }); - } - - bool twocolors = false; - if (nodes.get(redsucc->getID())) { // is red successor in Ap? - auto *apn = CAp.getNode(redsucc); - assert(apn); - if (CAp.blues.get(apn->getID())) { - twocolors = true; - } - CAp.reds.set(apn->getID()); - //DBG(cda, " - Ap red: " << redsucc->getID()); - } else { - foreachFirstReachable(nodes, redsucc, [&](CDNode *cur) { - auto *apn = CAp.getNode(cur); - assert(apn); - CAp.reds.set(apn->getID()); - if (CAp.blues.get(apn->getID())) { - twocolors = true; - } - //DBG(cda, " - Ap red: " << apn->getID() << " (" - // << cur->getID() << " in original)"); - }); - } - - if (twocolors) { - return {}; - } - - return CAp; - } - - static bool checkAp(CDGraph& Ap) { - //for (auto *nd : CAp.Ap) { - // DBG(tmp, ">> Ap: " << nd->getID() << " b: " << CAp.blues.get(nd->getID()) - // << " r: " << CAp.reds.get(nd->getID())); - //} - //for (auto *nd : CAp.Ap) { - // for (auto *succ: nd->successors()) { - // DBG(tmp, ">> Ap: " << nd->getID() << " -> " << succ->getID()); - // } - //} - - // we can have only a single node with multiple successors - CDNode *p = nullptr; - for (auto *n : Ap) { - if (p) { - assert(n->successors().size() == 1); - assert(n->getSingleSuccessor() != n); - } else { - if (n->successors().size() > 1) { - p = n; - } - } - } - assert(p && "No entry node of Ap"); - - // from the p node there are edges that go into a cycle - // that contains the rest of the nodes - std::set visited; - auto *n = *(p->successors().begin()); - auto *cur = n; - do { - assert(cur != p); - - bool notseen = visited.insert(cur).second; - assert(notseen && "Visited a node twice"); - (void) notseen; // c++17 TODO: replace with [[maybe_unused]] - - cur = cur->getSingleSuccessor(); - assert(cur && "Node on the cycle does not have a unique successor"); - } while (cur != n); - - assert(visited.size() == Ap.size() - 1 - && "Cycle does not contain all the nodes except p"); - return true; - } - - template - std::pair find(CDNode *start, CDNode *end, - const Pred1& P1, const Pred2& P2) { - CDNode *n1 = nullptr, *n2 = nullptr; - CDNode *n = start; - do { - if (P1(n)) { - n1 = n; - } - if (P2(n)) { - n2 = n; - break; - } - n = n->getSingleSuccessor(); - assert(n && "A node on the cycle has not a single successor"); - } while (n != end); - - return {n1, n2}; - } - - void computeDOD(ColoredAp& CAp, CDNode *p, ResultT& CD, ResultT& revCD, - bool asTernary = false) { - assert(checkAp(CAp.Ap)); // sanity check - - CDNode *b1 = nullptr, *b2 = nullptr, *b3 = nullptr; - CDNode *r1 = nullptr, *r2 = nullptr; - - // get some blue node to have a starting point - auto bid = *(CAp.blues.begin()); - //DBG(tmp, "Blue node with id " << bid); - b1 = CAp.Ap.getNode(bid); - assert(b1 && "The blue node is not on Ap"); - //DBG(tmp, "Starting from blue: " << b1->getID()); - assert(CAp.isBlue(b1)); - - - auto isblue = [&](CDNode *x) -> bool { return CAp.isBlue(x); }; - auto isred = [&](CDNode *x) -> bool { return CAp.isRed(x); }; - - std::tie(b2, r1) = find(b1, b1, isblue, isred); - std::tie(r2, b3) = find(r1, b1, isred, isblue); - if (b3 != nullptr) { - if (find(b3, b1, isred, isred).first != nullptr) { - // there is another red, no DOD - return; - } - } else { - b3 = b1; - } - - assert(b1); - assert(b2); - assert(b3); - assert(r1); - assert(r2); - - if (asTernary) { - constructTernaryRelation(CAp, p, CD, revCD, - b2, b3, r1, r2); - } else { // break into binary relation - constructBinaryRelation(CAp, p, CD, revCD, - b2, b3, r1, r2); - } - } - - void constructTernaryRelation(ColoredAp& CAp, CDNode *p, - ResultT& CD, ResultT& revCD, - CDNode *b2, CDNode *b3, - CDNode *r1, CDNode *r2) { - auto *cur = b2; - do { - auto *gcur = CAp.getGNode(cur); - assert(gcur); - - auto *ncur = r2; - do { - auto *gncur = CAp.getGNode(ncur); - assert(gncur); - //DBG(cda, p->getID() << " - dod -> {" << gcur->getID() << ", " - // << gncur->getID() << "}"); - - CD[gcur].insert(p); - CD[gncur].insert(p); - revCD[p].insert(gcur); - revCD[p].insert(gncur); - - ncur = ncur->getSingleSuccessor(); - } while (!(CAp.isBlue(ncur) || CAp.isRed(ncur))); - assert(ncur == b3); (void) b3; - - cur = cur->getSingleSuccessor(); - } while (!(CAp.isBlue(cur) || CAp.isRed(cur))); - assert(cur == r1); (void) r1; - } - - void constructBinaryRelation(ColoredAp& CAp, CDNode *p, - ResultT& CD, ResultT& revCD, - CDNode *b2, CDNode *b3, - CDNode *r1, CDNode *r2) { - auto *cur = b2; - do { - auto *gcur = CAp.getGNode(cur); - assert(gcur); - CD[gcur].insert(p); - revCD[p].insert(gcur); - //DBG(cda, p->getID() << " - dod -> " << gcur->getID()); - cur = cur->getSingleSuccessor(); - } while (!(CAp.isBlue(cur) || CAp.isRed(cur))); - assert(cur == r1); (void)r1; - - cur = r2; - do { - auto *gcur = CAp.getGNode(cur); - assert(gcur); - CD[gcur].insert(p); - revCD[p].insert(gcur); - //DBG(cda, p->getID() << " - dod -> " << gcur->getID()); - cur = cur->getSingleSuccessor(); - } while (!(CAp.isBlue(cur) || CAp.isRed(cur))); - assert(cur == b3); (void) b3; - } - -protected: - // make this public, so that we can use it in NTSCD+DOD algorithm - template - void computeDOD(CDNode *p, CDGraph& graph, - OnAllPaths& allpaths, - ResultT& CD, ResultT& revCD) { - assert(p->successors().size() == 2 && "We work with at most 2 successors"); - - DBG_SECTION_BEGIN(cda, "Creating Ap graph for fun " << graph.getName() << - " node " << p->getID()); - auto res = createColoredAp(allpaths, graph, p); - DBG_SECTION_END(cda, "Done creating Ap graph"); - if (res.Ap.empty()) { - DBG(cda, "No DOD in the Ap are possible"); - // no DOD possible - return; - } - - DBG(cda, "Computing DOD from the Ap"); - computeDOD(res, p, CD, revCD); - } - -public: - - std::pair compute(CDGraph& graph) { - ResultT CD; - ResultT revCD; - - DBG_SECTION_BEGIN(cda, "Computing DOD for all predicates"); - - AllMaxPath allmaxpath; - DBG_SECTION_BEGIN(cda, "Coputing nodes that are on all max paths from nodes for fun " - << graph.getName()); - auto allpaths = allmaxpath.compute(graph); - DBG_SECTION_END(cda, "Done computing nodes that are on all max paths from nodes"); - - for (auto *p : graph.predicates()) { - computeDOD(p, graph, allpaths, CD, revCD); - } - - DBG_SECTION_END(cda, "Finished computing DOD for all predicates"); - return {CD, revCD}; - } -}; - - -class DODRanganath { - //using ResultT = std::map>>; - // NOTE: although DOD is a ternary relation, we treat it as binary - // by breaking a->(b, c) to (a, b) and (a, c). It is less precise, - // but our API is not prepared for the ternary relation. - using ResultT = std::map>; - enum class Color { WHITE, BLACK, UNCOLORED }; - - struct Info { - Color color{Color::UNCOLORED}; - }; - - std::unordered_map data; - - void coloredDAG(CDGraph& graph, CDNode *n, std::set& visited) { - if (visited.insert(n).second) { - auto& successors = n->successors(); - if (successors.empty()) - return; - - for (auto *q : successors) { - coloredDAG(graph, q, visited); - } - auto *s = *(successors.begin()); - auto c = data[s].color; - for (auto *q : successors) { - if (data[q].color != c) { - c = Color::UNCOLORED; - break; - } - } - data[n].color = c; - } - } - - bool dependence(CDNode *n, CDNode *m, CDNode *p, CDGraph& G) { - for (auto *n : G) { - data[n].color = Color::UNCOLORED; - } - data[m].color = Color::WHITE; - data[p].color = Color::BLACK; - - std::set visited; - visited.insert(m); - visited.insert(p); - - coloredDAG(G, n, visited); - - bool whiteChild = false; - bool blackChild = false; - - for (auto *q : n->successors()) { - if (data[q].color == Color::WHITE) - whiteChild = true; - if (data[q].color == Color::BLACK) - blackChild = true; - } - - return whiteChild && blackChild; - } - - // the paper uses 'reachable', but it is wrong - // Keep the method for now anyway. - bool reachable(CDNode *from, CDNode *n) { - ADT::SetQueue> queue; - queue.push(from); - - while (!queue.empty()) { - auto *cur = queue.pop(); - if (n == cur) { - return true; - } - - for (auto *s : cur->successors()) { - queue.push(s); - } - } - return false; - } - - bool onallpaths(CDNode *from, CDNode *n, CDGraph& G) { - if (from == n) // fast path - return true; - - struct NodeInf { bool onstack{false}; bool visited{false}; }; - std::unordered_map data; - - data.reserve(G.size()); - - const std::function onallpths = [&](CDNode *node, CDNode *target) -> bool { - if (node == target) - return true; - - data[node].visited = true; - - if (!node->hasSuccessors()) - return false; - - for (auto *s : node->successors()) { - if (data[s].onstack) - return false; - if (!data[s].visited) { - data[s].onstack = true; - if (!onallpths(s, target)) - return false; - data[s].onstack = false; - } - } - // if we have successors and got here, - // then all successors reach target - return true; - }; - - data[from].onstack = true; - //DBG(tmp, "on all paths:" << from->getID() << ", " << n->getID() << ": " << r ); - return onallpths(from, n); - } - -public: - std::pair compute(CDGraph& graph) { - ResultT CD; - ResultT revCD; - - DBG(cda, "Computing DOD (Ranganath)"); - - data.reserve(graph.size()); - - for (auto *n : graph.predicates()) { - for (auto *m : graph) { - for (auto *p : graph) { - if (p == m) { - continue; - } - - if (onallpaths(m, p, graph) && onallpaths(p, m, graph) && - dependence(n, p, m, graph)) { - //DBG(cda, "DOD: " << n->getID() << " -> {" - // << p->getID() << ", " << m->getID() << "}"); - CD[m].insert(n); - CD[p].insert(n); - revCD[n].insert(m); - revCD[n].insert(n); - } - } - } - } - - return {CD, revCD}; - } -}; - -}; - -#endif diff --git a/include/dg/ControlDependence/DODNTSCD.h b/include/dg/ControlDependence/DODNTSCD.h deleted file mode 100644 index 51fd2cc6c..000000000 --- a/include/dg/ControlDependence/DODNTSCD.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef DG_DODNTSCD_H_ -#define DG_DODNTSCD_H_ - -#include -#include -#include - -#include "CDGraph.h" -#include "DOD.h" - -namespace dg { - -class DODNTSCD : public DOD { - using ResultT = DOD::ResultT; - - template - void computeNTSCD(CDNode *p, CDGraph& graph, - const OnAllPathsT& onallpaths, - ResultT& CD, ResultT& revCD) { - auto& succs = p->successors(); - assert(succs.size() == 2); - auto succit = succs.begin(); - auto *s1 = *succit; - auto *s2 = *(++succit); - assert(++succit == succs.end()); - - auto it1 = onallpaths.find(s1); - if (it1 == onallpaths.end()) - return; - auto it2 = onallpaths.find(s2); - if (it2 == onallpaths.end()) - return; - - const auto& nodes1 = it1->second; - const auto& nodes2 = it2->second; - // FIXME: we could do that faster - for (auto *n : graph) { - if (nodes1.get(n->getID()) ^ nodes2.get(n->getID())) { - CD[n].insert(p); - revCD[p].insert(n); - } - } - } - -public: - - std::pair compute(CDGraph& graph) { - ResultT CD; - ResultT revCD; - - DBG_SECTION_BEGIN(cda, "Computing DOD for all predicates"); - - AllMaxPath allmaxpath; - DBG_SECTION_BEGIN(cda, "Coputing nodes that are on all max paths from nodes for fun " - << graph.getName()); - auto allpaths = allmaxpath.compute(graph); - DBG_SECTION_END(cda, "Done coputing nodes that are on all max paths from nodes"); - - for (auto *p : graph.predicates()) { - computeDOD(p, graph, allpaths, CD, revCD); - computeNTSCD(p, graph, allpaths, CD, revCD); - } - - DBG_SECTION_END(cda, "Finished computing DOD for all predicates"); - return {CD, revCD}; - } -}; - -}; - -#endif diff --git a/include/dg/ControlDependence/NTSCD.h b/include/dg/ControlDependence/NTSCD.h deleted file mode 100644 index f74ba4316..000000000 --- a/include/dg/ControlDependence/NTSCD.h +++ /dev/null @@ -1,305 +0,0 @@ -#ifndef DG_NTSCD_H -#define DG_NTSCD_H - -#include -#include -#include -#include - -#include "CDGraph.h" -#include "dg/ADT/Queue.h" -#include "dg/ADT/SetQueue.h" - -namespace dg { - -class NTSCD { - using ResultT = std::map>; - - struct Info { - unsigned colored{false}; - unsigned short counter; - }; - - std::unordered_map data; - - void compute(CDGraph& graph, CDNode *target) { - - // initialize nodes - for (auto *nd : graph) { - auto &D = data[nd]; - D.colored = false; - D.counter = nd->successors().size(); - } - - // initialize the search - data[target].colored = true; - ADT::QueueLIFO queue; - queue.push(target); - - // search! - while (!queue.empty()) { - auto *node = queue.pop(); - assert(data[node].colored && "A non-colored node in queue"); - - for (auto *pred : node->predecessors()) { - auto& D = data[pred]; - --D.counter; - if (D.counter == 0) { - D.colored = true; - queue.push(pred); - } - } - } - } - -public: - - // returns control dependencies and reverse control dependencies - std::pair compute(CDGraph& graph) { - ResultT CD; - ResultT revCD; - - data.reserve(graph.size()); - - for (auto *nd : graph) { - compute(graph, nd); - - for (auto *predicate : graph.predicates()) { - bool has_colored = false; - bool has_uncolored = false; - for (auto *succ : predicate->successors()) { - if (data[succ].colored) - has_colored = true; - if (!data[succ].colored) - has_uncolored = true; - } - - if (has_colored && has_uncolored) { - CD[nd].insert(predicate); - revCD[predicate].insert(nd); - } - } - } - - return {CD, revCD}; - } -}; - - -class NTSCD2 { - using ResultT = std::map>; - - struct Info { - unsigned color{0}; - }; - - std::unordered_map data; - - void compute(CDGraph& graph, CDNode *target, ResultT& CD, ResultT& revCD) { - std::set frontier; - std::set new_frontier; - - // color the target node - data[target].color = target->getID(); - for (auto *pred : target->predecessors()) { - if (data[pred].color != target->getID()) { - frontier.insert(pred); - } - } - - bool progress; - do { - progress = false; - new_frontier.clear(); - - for (auto *nd : frontier) { - assert(!nd->successors().empty()); - // do all successors have the right color? - bool colorit = true; - for (auto *succ : nd->successors()) { - if (data[succ].color != target->getID()) { - colorit = false; - break; - } - } - - // color the node and enqueue its predecessors - if (colorit) { - data[nd].color = target->getID(); - for (auto *pred : nd->predecessors()) { - if (data[pred].color != target->getID()) { - new_frontier.insert(pred); - } - } - progress = true; - } else { - // re-queue the node as nothing happend - new_frontier.insert(nd); - } - } - - new_frontier.swap(frontier); - } while (progress); - - for (auto *predicate : graph.predicates()) { - bool has_colored = false; - bool has_uncolored = false; - for (auto *succ : predicate->successors()) { - if (data[succ].color == target->getID()) - has_colored = true; - if (data[succ].color != target->getID()) - has_uncolored = true; - } - - if (has_colored && has_uncolored) { - CD[target].insert(predicate); - revCD[predicate].insert(target); - } - } - } - -public: - - // returns control dependencies and reverse control dependencies - std::pair compute(CDGraph& graph) { - ResultT CD; - ResultT revCD; - - data.reserve(graph.size()); - - for (auto *nd : graph) { - compute(graph, nd, CD, revCD); - } - - return {CD, revCD}; - } -}; - -/// Implementation of the original algorithm for the computation of NTSCD -/// that is due to Ranganath et al. This algorithm is wrong and -/// can compute incorrect results (it behaves differently when -/// LIFO or FIFO or some other type of queue is used). -class NTSCDRanganath { - using ResultT = std::map>; - - // symbol t_{mn} - struct Symbol : public std::pair { - Symbol(CDNode *a, CDNode *b) : std::pair(a, b) {} - }; - - std::unordered_map>> S; - - ADT::SetQueue> workbag; - - bool processNode(CDGraph&graph, CDNode *n) { - bool changed = false; - auto *s = n->getSingleSuccessor(); - if (s && s != n) { // (2.1) single successor case - for (auto *p : graph.predicates()) { - auto& Ssp = S[s][p]; - for (const Symbol& symb : S[n][p]) { - if (Ssp.insert(symb).second) { - DBG(tmp, "(1) S[" << s->getID() << ", " << p->getID() - << "] <- t(" << symb.first->getID() << ", " - << symb.second->getID() << ")"); - DBG(tmp, "(2.1) queuing node: " << s->getID()); - changed = true; - workbag.push(s); - } - } - } - } else if (n->successors().size() > 1) { // (2.2) multiple successors case - for (auto *m : graph) { - auto& Smn = S[m][n]; - if (Smn.size() == n->successors().size()) { - for (auto *p : graph.predicates()) { - if (p == n) { - continue; - } - auto& Smp = S[m][p]; - for (const Symbol& symb : S[n][p]) { - if (Smp.insert(symb).second) { - changed = true; - workbag.push(m); - DBG(tmp, "(1) S[" << m->getID() << ", " << p->getID() - << "] <- t(" << symb.first->getID() << ", " - << symb.second->getID() << ")"); - DBG(tmp, "(2.2) queuing node: " << m->getID()); - } - } - } - } - } - } - - return changed; - } - -public: - - // returns control dependencies and reverse control dependencies - // doFixpoint turns on the fix of the ranganath's algorithm - // XXX: we should create a new fixed algorithm completely, as we do not need - // the workbag and so on. - std::pair compute(CDGraph& graph, bool doFixpoint = true) { - ResultT CD; - ResultT revCD; - - S.reserve(2*graph.predicates().size()); - - // (1) initialize - for (auto *p : graph.predicates()) { - for (auto *n : p->successors()) { - S[n][p].insert(Symbol{p, n}); - //DBG(tmp, "(1) S[" << n->getID() << ", " << p->getID() - // << "] <- t(" << p->getID() << ", " << n->getID() << ")"); - //DBG(tmp, "(1) queuing node: " << n->getID()); - workbag.push(n); - } - } - - // (2) calculate all-path reachability - if (doFixpoint) { - DBG(cda, "Performing fixpoint of Ranganath's algorithm"); - bool changed; - do { - changed = false; - for (auto *n : graph) { - changed |= processNode(graph, n); - } - } while (changed); - } else { - DBG(cda, "Running the original (wrong) Ranganath's algorithm"); - while (!workbag.empty()) { - auto *n = workbag.pop(); - //DBG(cda, "Processing node: " << n->getID()); - processNode(graph, n); - } - } - - // (3) compute NTSCD - for (auto *n : graph) { - for (auto *p : graph.predicates()) { - auto& Snp = S[n][p]; - - //DBG(tmp, "(1) S[" << n->getID() << ", " << p->getID() << "] ="); - //for (auto & symb : Snp) { - // DBG(tmp, " t(" << symb.first->getID() << ", " << symb.second->getID() << ")"); - //} - if (Snp.size() > 0 && Snp.size() < p->successors().size()) { - CD[n].insert(p); - revCD[p].insert(n); - } - } - } - - return {CD, revCD}; - } -}; - - - -} // namespace dg - -#endif // DG_NTSCD_H diff --git a/include/dg/DFS.h b/include/dg/DFS.h deleted file mode 100644 index bc272e8ee..000000000 --- a/include/dg/DFS.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef DG_DFS_H_ -#define DG_DFS_H_ - -#include "dg/NodesWalk.h" -#include "dg/ADT/Queue.h" - -using dg::ADT::QueueLIFO; - -namespace dg { - -template , - typename EdgeChooser = SuccessorsEdgeChooser > -struct DFS : public NodesWalk, VisitTracker, EdgeChooser> { - DFS() = default; - DFS(EdgeChooser chooser) : NodesWalk, VisitTracker, EdgeChooser>(std::move(chooser)) {} - DFS(VisitTracker tracker) : NodesWalk, VisitTracker, EdgeChooser>(std::move(tracker)) {} - DFS(VisitTracker tracker, EdgeChooser chooser) - : NodesWalk, VisitTracker, EdgeChooser>(std::move(tracker), std::move(chooser)) {} -}; - -} // namespace dg - -#endif diff --git a/include/dg/DG2Dot.h b/include/dg/DG2Dot.h deleted file mode 100644 index 3b04e5fea..000000000 --- a/include/dg/DG2Dot.h +++ /dev/null @@ -1,639 +0,0 @@ -#ifndef DG_2_DOT_H_ -#define DG_2_DOT_H_ - -#include -#include -#include - -#include "dg/DependenceGraph.h" -#include "dg/DFS.h" - -namespace dg { -namespace debug { - -enum dg2dot_options { - PRINT_NONE = 0, // print no edges - PRINT_CFG = 1 << 0, - PRINT_REV_CFG = 1 << 1, - PRINT_DD = 1 << 2, - PRINT_REV_DD = 1 << 3, - PRINT_USE = 1 << 4, - PRINT_USER = 1 << 5, - PRINT_CD = 1 << 6, - PRINT_REV_CD = 1 << 7, - PRINT_ID = 1 << 8, - PRINT_REV_ID = 1 << 9, - PRINT_CALL = 1 << 10, - PRINT_POSTDOM = 1 << 11, - PRINT_ALL = 0xfff -}; - -struct Indent -{ - int ind; - Indent(int ind = 1):ind(ind) {} - friend std::ostream& operator <<(std::ostream& os, const Indent& ind); -}; - -std::ostream& operator <<(std::ostream& os, const Indent& ind) -{ - for (int i = 0; i < ind.ind; ++i) - os << "\t"; - - return os; -} - -template -class DG2Dot -{ - std::set::ContainerType *> dumpedGlobals; - // slicing criteria - std::set criteria; -public: - using KeyT = typename NodeT::KeyType; - - DG2Dot(DependenceGraph *dg, - uint32_t opts = PRINT_CFG | PRINT_DD | PRINT_CD | PRINT_USE, - const char *file = NULL) - : options(opts), dg(dg), file(file) - { - // if a graph has no global nodes, this will forbid trying to print them - dumpedGlobals.insert(nullptr); - reopen(file); - } - - void setSlicingCriteria(const std::set& crit) { - criteria = crit; - } - - bool open(const char *new_file) - { - if (out.is_open()) { - std::cerr << "File already opened (" << file << ")" - << std::endl; - return false; - } else - reopen(new_file); - } - - virtual std::ostream& printKey(std::ostream& os, KeyT key) - { - os << key; - return os; - } - - // \return - error state: true if there's an error, false otherwise - virtual bool checkNode(std::ostream& os, NodeT *node) - { - bool err = false; - - if (!node->getBBlock()) { - err = true; - os << "\\nERR: no BB"; - } - - return err; - } - - bool ensureFile(const char *fl) - { - if (fl) - reopen(fl); - - if (!out.is_open()) { - std::cerr << "File '" << file << "' not opened" - << std::endl; - return false; - } - - return true; - } - - virtual bool dump(const char *new_file = nullptr, - const char *only_functions = nullptr) - { - (void) only_functions; - - if (!ensureFile(new_file)) - return false; - - start(); - - dumpBBs(dg); - - // even when we have printed nodes while - // going through BBs, print nodes again, - // so that we'll see if there are any nodes - // that are not in BBs - dump_nodes(); - dump_edges(); - - // print subgraphs once we printed all the nodes - if (!subgraphs.empty()) - out << "\n\t/* ----------- SUBGRAPHS ---------- */\n\n"; - for (auto sub : subgraphs) { - dump_subgraph(sub); - } - - - end(); - - out.close(); - return true; - } - - /* if user want's manual printing, he/she can */ - - void start() - { - out << "digraph \"DependenceGraph\" {\n"; - out << "\tcompound=true label=\"Graph " << dg - << " has " << dg->size() << " nodes\\n\n" - << "\tdd edges color: " << dd_color << "\n" - << "\tuse edges color: " << use_color << ", dashed\n" - << "\tcd edges color: " << cd_color << "\n" - << "\tcfg edges color: " << cfg_color << "\"\n\n"; - } - - void end() - { - out << "}\n"; - } - - void dumpSubgraphStart(DependenceGraph *sub, - const char *name = nullptr) - { - out << "\t/* subgraph " << sub << " nodes */\n"; - out << "\tsubgraph cluster_" << sub << " {\n"; - out << "\t\tstyle=\"filled, rounded\" fillcolor=gray95\n"; - out << "\t\tlabel=\"Subgraph "; - if (name) - out << name << " "; - - out << "[" << sub << "]" - << "\\nhas " << sub->size() << " nodes\n"; - - uint64_t slice_id = sub->getSlice(); - if (slice_id != 0) - out << "\\nslice: "<< slice_id; - - out << "\"\n"; - - - // dump BBs of the formal parameters - dump_parameters(sub, 2); - } - - void dumpSubgraphEnd(DependenceGraph *sub, bool with_nodes = true) - { - if (with_nodes) { - // dump all nodes, to get it without BBlocks - // (we may not have BBlocks or we just don't want - // to print them - for (auto& I : *sub) { - dump_node(I.second, 2); - dump_node_edges(I.second, 2); - } - - if (dumpedGlobals.insert(sub->getGlobalNodes().get()).second) { - for (auto& I : *sub->getGlobalNodes()) { - dump_node(I.second, 2, "GLOB"); - dump_node_edges(I.second, 2); - } - } - } - - out << "\t}\n"; - } - - void dumpSubgraph(DependenceGraph *sub) - { - dumpSubgraphStart(sub); - dumpSubgraphEnd(sub); - } - - void dumpBBlock(BBlock *BB, int ind = 2) - { - dumpBB(BB, ind); - } - - void dumpBBlockEdges(BBlock *BB, int ind = 1) - { - dumpBBedges(BB, ind); - } - -private: - // what all to print? - uint32_t options; - - void reopen(const char *new_file) - { - if (!new_file) - new_file = "/dev/stdout"; - - if (out.is_open()) - out.close(); - - out.open(new_file); - file = new_file; - } - - void dumpBB(const BBlock *BB, int indent) - { - Indent Ind(indent); - - out << Ind << "/* Basic Block "; - printKey(out, BB->getKey()); - out << " [" << BB << "] */\n"; - out << Ind << "subgraph cluster_bb_" << BB << " {\n"; - out << Ind << "\tstyle=filled fillcolor=white\n"; - out << Ind << "\tlabel=\""; - - printKey(out, BB->getKey()); - out << " [" << BB << "]"; - - unsigned int dfsorder = BB->getDFSOrder(); - if (dfsorder != 0) - out << Ind << "\\ndfs order: "<< dfsorder; - - uint64_t slice_id = BB->getSlice(); - if (slice_id != 0) - out << "\\nslice: "<< slice_id; - - out << "\"\n"; - - for (NodeT *n : BB->getNodes()) { - // print nodes in BB, edges will be printed later - out << Ind << "\tNODE" << n - << " [shape=rect label=\"" << n->getKey() << "\"]\n"; - } - - out << Ind << "} /* cluster_bb_" << BB << " */\n\n"; - } - - void dumpBBedges(BBlock *BB, int indent) - { - Indent Ind(indent); - - if (options & PRINT_CFG) { - for (auto S : BB->successors()) { - NodeT *lastNode = BB->getLastNode(); - NodeT *firstNode = S.target->getFirstNode(); - - out << Ind - << "NODE" << lastNode << " -> " - << "NODE" << firstNode - << " [penwidth=2 label=\"" << static_cast(S.label) << "\"" - << " ltail=cluster_bb_" << BB - << " lhead=cluster_bb_" << S.target - << " color=\"" << cfg_color << "\"" << "]\n"; - } - } - - if (options & PRINT_REV_CFG) { - for (auto S : BB->predecessors()) { - NodeT *lastNode = S->getLastNode(); - NodeT *firstNode = BB->getFirstNode(); - - out << Ind - << "NODE" << firstNode << " -> " - << "NODE" << lastNode - << " [penwidth=2 color=\"" << cfg_color << "\" dashed" - << " ltail=cluster_bb_" << BB - << " lhead=cluster_bb_" << S << " constraint=false]\n"; - } - } - - if (options & PRINT_CD) { - for (auto S : BB->controlDependence()) { - NodeT *lastNode = BB->getLastNode(); - NodeT *firstNode = S->getFirstNode(); - - out << Ind - << "NODE" << lastNode << " -> " - << "NODE" << firstNode - << " [penwidth=2 color=blue" - << " ltail=cluster_bb_" << BB - << " lhead=cluster_bb_" << S << "]\n"; - } - - for (BBlock *S : BB->getPostDomFrontiers()) { - NodeT *start = BB->getFirstNode(); - NodeT *end = S->getLastNode(); - - out << Ind - << "/* post-dominance frontiers */\n" - << "NODE" << start << " -> " - << "NODE" << end - << " [penwidth=3 color=green" - << " ltail=cluster_bb_" << BB - << " lhead=cluster_bb_" << S << " constraint=false]\n"; - } - } - - if (options & PRINT_POSTDOM) { - BBlock *ipd = BB->getIPostDom(); - if (ipd) { - NodeT *firstNode = BB->getFirstNode(); - NodeT *lastNode = ipd->getLastNode(); - - out << Ind - << "NODE" << lastNode << " -> " - << "NODE" << firstNode - << " [penwidth=3 color=purple" - << " ltail=cluster_bb_" << BB - << " lhead=cluster_bb_" << ipd << " constraint=false]\n"; - } - } - } - - void dump_parameters(NodeT *node, int ind) - { - DGParameters *params = node->getParameters(); - - if (params) { - dump_parameters(params, ind, false); - } - } - - void dump_parameters(DependenceGraph *g, int ind) - { - DGParameters *params = g->getParameters(); - - if (params) { - dump_parameters(params, ind, true); - } - } - - void dump_parameters(DGParameters *params, int ind, bool formal) - { - Indent Ind(ind); - - // FIXME - // out << Ind << "/* Input parameters */\n"; - // dumpBB(params->getBBIn(), data); - // out << Ind << "/* Output parameters */\n"; - // dumpBB(params->getBBOut(), data); - - // dump all the nodes again to get the names - for (auto it : *params) { - auto& p = it.second; - if (p.in) { - dump_node(p.in, ind, formal ? "[f] IN ARG" : "IN ARG"); - dump_node_edges(p.in, ind); - } else - out << "NO IN ARG"; - - if (p.out) { - dump_node(p.out, ind, formal ? "[f] OUT ARG" : "OUT ARG"); - dump_node_edges(p.out, ind); - } else - out << "NO OUT ARG"; - } - - for (auto I = params->global_begin(), E = params->global_end(); - I != E; ++I) { - auto& p = I->second; - if (p.in) { - dump_node(p.in, ind, formal ? "[f] GLOB IN" : "GLOB IN"); - dump_node_edges(p.in, ind); - } else - out << "NO GLOB IN ARG"; - - if (p.out) { - dump_node(p.out, ind, formal ? "[f] GLOB OUT" : "GLOB OUT"); - dump_node_edges(p.out, ind); - } else - out << "NO GLOB OUT ARG"; - } - - auto p = params->getVarArg(); - if (p) { - if (p->in) { - dump_node(p->in, ind, "[va] IN ARG"); - dump_node_edges(p->in, ind); - } else - out << "NO IN va ARG"; - - if (p->out) { - dump_node(p->out, ind, "[va] OUT ARG"); - dump_node_edges(p->out, ind); - } else - out << "NO OUT ARG"; - } - - if (auto noret = params->getNoReturn()) { - dump_node(noret, ind, "[noret]"); - dump_node_edges(noret, ind); - } - } - - void dump_subgraph(DependenceGraph *sub) - { - dumpSubgraphStart(sub); - - // dump BBs in the subgraph - dumpBBs(sub, 2); - - // dump all nodes again, if there is any that is - // not in any BB - for (auto& I : *sub) - dump_node(I.second, 2); - // dump edges between nodes - for (auto& I : *sub) - dump_node_edges(I.second, 2); - - dumpSubgraphEnd(sub); - } - - void dumpBBs(DependenceGraph *graph, int ind = 1) - { - for (auto it : graph->getBlocks()) - dumpBB(it.second, ind); - - // print CFG edges between BBs - if (options & (PRINT_CFG | PRINT_REV_CFG)) { - out << Indent(ind) << "/* CFG edges */\n"; - for (auto it : graph->getBlocks()) - dumpBBedges(it.second, ind); - } - } - - void dump_node(NodeT *node, int ind = 1, const char *prefix = nullptr) - { - bool err = false; - unsigned int dfsorder = node->getDFSOrder(); - unsigned int bfsorder = node->getDFSOrder(); - uint32_t slice_id = node->getSlice(); - Indent Ind(ind); - - out << Ind - << "NODE" << node << " [label=\""; - - if (prefix) - out << prefix << " "; - - printKey(out, node->getKey()); - - if (node->hasSubgraphs()) - out << "\\nsubgraphs: " << node->subgraphsNum(); - if (dfsorder != 0) - out << "\\ndfs order: "<< dfsorder; - if (bfsorder != 0) - out << "\\nbfs order: "<< bfsorder; - - if (slice_id != 0) - out << "\\nslice: "<< slice_id; - - // check if the node is OK, and if not - // highlight it - err = checkNode(out, node); - - // end of label - out << "\" "; - - if (err) { - out << "style=filled fillcolor=red"; - } else if (criteria.count(node) > 0) { - out << "style=filled fillcolor=orange"; - } else if (slice_id != 0) - out << "style=filled fillcolor=greenyellow"; - else - out << "style=filled fillcolor=white"; - - out << "]\n"; - - dump_parameters(node, ind); - if (node->hasSubgraphs() && (options & PRINT_CALL)) { - // add call-site to callee edges - for (auto subgraph : node->getSubgraphs()) { - out << Ind - << "NODE" << node - << " -> NODE" << subgraph->getEntry() - << " [label=\"call\"" - << " lhead=cluster_" << subgraph - << " penwidth=3 style=dashed]\n"; - } - } - } - - void dump_nodes() - { - out << "\t/* nodes */\n"; - for (auto& I : *dg) { - auto node = I.second; - - dump_node(node); - - for (auto subgraph : node->getSubgraphs()) { - subgraphs.insert(subgraph); - } - } - - if (dumpedGlobals.insert(dg->getGlobalNodes().get()).second) - for (auto& I : *dg->getGlobalNodes()) - dump_node(I.second, 1, "GL"); - } - - void dump_edges() - { - for (auto& I : *dg) { - dump_node_edges(I.second); - } - - if (dumpedGlobals.insert(dg->getGlobalNodes().get()).second) - for (auto& I : *dg->getGlobalNodes()) - dump_node_edges(I.second); - } - - void dump_node_edges(NodeT *n, int ind = 1) - { - Indent Ind(ind); - - out << Ind << "/* -- node " << n->getKey() << "\n" - << Ind << " * ------------------------------------------- */\n"; - - if (options & PRINT_DD) { - out << Ind << "/* DD edges */\n"; - for (auto II = n->data_begin(), EE = n->data_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << dd_color << "\" rank=max]\n"; - } - - if (options & PRINT_REV_DD) { - out << Ind << "/* reverse DD edges */\n"; - for (auto II = n->rev_data_begin(), EE = n->rev_data_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << dd_color << "\" style=\"dashed\" constraint=false]\n"; - } - - if (options & PRINT_USE) { - out << Ind << "/* USE edges */\n"; - for (auto II = n->use_begin(), EE = n->use_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << use_color << "\" rank=max style=\"dashed\"]\n"; - } - - if (options & PRINT_USER) { - out << Ind << "/* user edges */\n"; - for (auto II = n->user_begin(), EE = n->user_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << use_color << "\" style=\"dashed\" constraint=false]\n"; - } - - - if (options & PRINT_CD) { - out << Ind << "/* CD edges */\n"; - for (auto II = n->control_begin(), EE = n->control_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << cd_color << "\"]\n"; - } - - if (options & PRINT_REV_CD) { - out << Ind << "/* reverse CD edges */\n"; - for (auto II = n->rev_control_begin(), EE = n->rev_control_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"" << cd_color << "\" style=\"dashed\" constraint=false]\n"; - } - - if (options & PRINT_ID) { - out << Ind << "/* ID edges */\n"; - for (auto II = n->interference_begin(), EE = n->interference_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"red\" constraint=false]\n"; - } - - if (options & PRINT_REV_ID) { - out << Ind << "/* reverse ID edges */\n"; - for (auto II = n->rev_interference_begin(), EE = n->rev_interference_end(); - II != EE; ++II) - out << Ind << "NODE" << n << " -> NODE" << *II - << " [color=\"orange\" constraint=false]\n"; - } - } - - const char *dd_color = "cyan4"; - const char *use_color = "black"; - const char *cd_color = "blue"; - const char *cfg_color = "gray"; - - DependenceGraph *dg; - const char *file; - std::set *> subgraphs; - -protected: - std::ofstream out; -}; - -} // debug -} // namespace dg - -#endif // DG_2_DOT_H_ - diff --git a/include/dg/DGParameters.h b/include/dg/DGParameters.h deleted file mode 100644 index 8e93b8692..000000000 --- a/include/dg/DGParameters.h +++ /dev/null @@ -1,248 +0,0 @@ -#ifndef _DG_PARAMETERS_H_ -#define _DG_PARAMETERS_H_ - -#include -#include -#include - -#include "BBlock.h" - -namespace dg { - - -template -struct DGParameterPair -{ - DGParameterPair(NodeT *v1, NodeT *v2) - : in(v1), out(v2) {} - - // input value of parameter - NodeT *in; - // output value of parameter - NodeT *out; - - void removeIn() - { - if (in) { - in->isolate(); - delete in; - in = nullptr; - } - } - - void removeOut() - { - if (out) { - out->isolate(); - delete out; - out = nullptr; - } - } -}; - -// -------------------------------------------------------- -// --- Parameters of functions -// -// DGParameters keep list of function parameters (arguments). -// Each parameter is a pair - input and output value and is -// represented as a node in the dependence graph. -// Moreover, there are BBlocks for input and output parameters -// so that the parameters can be used in BBlock analysis -// -------------------------------------------------------- -template -class DGParameters -{ -public: - using KeyT = typename NodeT::KeyType; - using ContainerType = std::map>; - using iterator = typename ContainerType::iterator; - using const_iterator = typename ContainerType::const_iterator; - - DGParameters(NodeT *cs = nullptr) - : BBIn(new BBlock), BBOut(new BBlock), callSite(cs){} - - ~DGParameters() - { - // delete the parameters itself - for (const auto& par : *this) { - delete par.second.in; - delete par.second.out; - } - - // delete globals parameters - for (const auto& gl : globals) { - delete gl.second.in; - delete gl.second.out; - } - - // delete auxiliary basic blocks - delete BBIn; - delete BBOut; - } - - DGParameterPair *operator[](KeyT k) { return find(k); } - const DGParameterPair *operator[](KeyT k) const { return find(k); } - - template - std::pair - construct(KeyT k, Args... args) { - auto in = new NodeT(args...); - auto out = new NodeT(args...); - add(k, in, out, ¶ms); - return {in, out}; - } - - template - std::pair - constructGlobal(KeyT k, Args... args) { - auto in = new NodeT(args...); - auto out = new NodeT(args...); - add(k, in, out, &globals); - return {in, out}; - } - - DGParameterPair *findGlobal(KeyT k) - { - return find(k, &globals); - } - - DGParameterPair *findParameter(KeyT k) - { - return find(k, ¶ms); - } - - DGParameterPair *find(KeyT k) - { - auto ret = findParameter(k); - if (!ret) - return findGlobal(k); - - return ret; - } - - const DGParameterPair *findParameter(KeyT k) const - { - return findParameter(k); - } - - const DGParameterPair *findGlobal(KeyT k) const { return findGlobal(k); } - const DGParameterPair *find(KeyT k) const { return find(k); } - - void remove(KeyT k) - { - params.erase(k); - } - - void removeIn(KeyT k) - { - auto p = find(k); - if (!p) - return; - - p->removeIn(); - - // if we do not have the other, just remove whole param - if (!p->out) - params.erase(k); - } - - void removeOut(KeyT k) - { - auto p = find(k); - if (!p) - return; - - p->removeOut(); - - // if we do not have the other, just remove whole param - if (!p->in) - params.erase(k); - } - - size_t paramsNum() const { return params.size(); } - size_t globalsNum() const { return globals.size(); } - size_t size() const { return params.size() + globals.size(); } - - iterator begin(void) { return params.begin(); } - const_iterator begin(void) const { return params.begin(); } - iterator end(void) { return params.end(); } - const_iterator end(void) const { return params.end(); } - - iterator global_begin() { return globals.begin(); } - const_iterator global_begin() const { return globals.begin(); } - iterator global_end() { return globals.end(); } - const_iterator global_end() const { return globals.end(); } - - const BBlock *getBBIn() const { return BBIn; } - const BBlock *getBBOut() const { return BBOut; } - BBlock *getBBIn() { return BBIn; } - BBlock *getBBOut() { return BBOut; } - - DGParameterPair* getVarArg() { return vararg.get(); } - const DGParameterPair* getVarArg() const { return vararg.get(); } - bool setVarArg(NodeT *in, NodeT *out) - { - assert(!vararg && "Already has a vararg parameter"); - - vararg.reset(new DGParameterPair(in, out)); - return true; - } - - NodeT* getNoReturn() { return noret.get(); } - const NodeT* getNoReturn() const { return noret.get(); } - bool addNoReturn(NodeT *n) - { - assert(!noret && "Already has the noret parameter"); - - noret.reset(n); - return true; - } - - const NodeT *getCallSite() const { return callSite; } - NodeT *getCallSite() { return callSite; } - void setCallSite(NodeT *n) { return callSite = n; } - -private: - // globals represented as a parameter - ContainerType globals; - // usual parameters - ContainerType params; - - // this is parameter that represents - // formal vararg parameters. It is only one, because without - // any further analysis, we cannot tell apart the formal varargs - std::unique_ptr> vararg{}; - // node representing that the function may not return - // -- we can add control dependencies to this node - std::unique_ptr noret{}; - - BBlock *BBIn; - BBlock *BBOut; - NodeT *callSite; - - DGParameterPair *find(KeyT k, ContainerType *C) - { - iterator it = C->find(k); - if (it == C->end()) - return nullptr; - - return &(it->second); - } - - bool add(KeyT k, NodeT *val_in, NodeT *val_out, ContainerType *C) - { - auto v = std::make_pair(k, DGParameterPair(val_in, val_out)); - if (!C->insert(v).second) - // we already has param with this key - return false; - - BBIn->append(val_in); - BBOut->append(val_out); - - return true; - } -}; - -} // namespace dg - -#endif // _DG_PARAMETERS_H_ diff --git a/include/dg/DataDependence/DataDependence.h b/include/dg/DataDependence/DataDependence.h deleted file mode 100644 index a26fd1b9b..000000000 --- a/include/dg/DataDependence/DataDependence.h +++ /dev/null @@ -1,72 +0,0 @@ -#ifndef DG_DATA_DEPENDENCE_ANALYSIS_H_ -#define DG_DATA_DEPENDENCE_ANALYSIS_H_ - -#include -#include - -#include "dg/Offset.h" -#include "dg/DataDependence/DataDependenceAnalysisImpl.h" -#include "dg/DataDependence/DataDependenceAnalysisOptions.h" -#include "dg/ReadWriteGraph/ReadWriteGraph.h" -#include "dg/MemorySSA/MemorySSA.h" - -#include "dg/util/debug.h" - -namespace dg { -namespace dda { - -// here the types are for type-checking (optional - user can do it -// when building the graph) and for later optimizations - -class DataDependenceAnalysis { - - // the implementation either uses reaching definitions - // or transformation to SSA - std::unique_ptr _impl; - - DataDependenceAnalysisImpl * - createAnalysis(ReadWriteGraph&& graph, - const DataDependenceAnalysisOptions& opts) { - assert(opts.isSSA() && "Unsupported analysis"); - return new MemorySSATransformation(std::move(graph), opts); - } - - const DataDependenceAnalysisOptions& _options; - -public: - DataDependenceAnalysis(ReadWriteGraph&& graph, - const DataDependenceAnalysisOptions& opts) - : _impl(createAnalysis(std::move(graph), opts)), _options(opts) {} - - DataDependenceAnalysis(ReadWriteGraph&& graph) - : DataDependenceAnalysis(std::move(graph), {}) {} - - ReadWriteGraph *getGraph() { return _impl->getGraph(); } - const ReadWriteGraph *getGraph() const { return _impl->getGraph(); } - - // run the analysis - void run() { _impl->run(); } - - // return the reaching definitions of ('mem', 'off', 'len') - // at the location 'where' - std::vector getDefinitions(RWNode *where, RWNode *mem, - const Offset& off, const Offset& len) { - return _impl->getDefinitions(where, mem, off, len); - } - - // return reaching definitions of a node that represents - // the given use - std::vector getDefinitions(RWNode *use) { - return _impl->getDefinitions(use); - } - - const DataDependenceAnalysisOptions& getOptions() const { return _options; } - - DataDependenceAnalysisImpl *getImpl() { return _impl.get(); } - const DataDependenceAnalysisImpl *getImpl() const { return _impl.get(); } -}; - -} // namespace dda -} // namespace dg - -#endif // DG_DATA_DEPENDENCE_ANALYSIS_H_ diff --git a/include/dg/DataDependence/DataDependenceAnalysisImpl.h b/include/dg/DataDependence/DataDependenceAnalysisImpl.h deleted file mode 100644 index f3060e966..000000000 --- a/include/dg/DataDependence/DataDependenceAnalysisImpl.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef DG_DATA_DEPENDENCE_ANALYSIS_IMPL_H_ -#define DG_DATA_DEPENDENCE_ANALYSIS_IMPL_H_ - -#include - -#include "dg/Offset.h" -#include "dg/DataDependence/DataDependenceAnalysisOptions.h" -#include "dg/ReadWriteGraph/ReadWriteGraph.h" - -#include "dg/util/debug.h" - -namespace dg { -namespace dda { - -// here the types are for type-checking (optional - user can do it -// when building the graph) and for later optimizations - -class DataDependenceAnalysisImpl { -protected: - ReadWriteGraph graph; - - const DataDependenceAnalysisOptions options; - -public: - DataDependenceAnalysisImpl(ReadWriteGraph&& graph, - const DataDependenceAnalysisOptions& opts) - : graph(std::move(graph)), options(opts) { - assert(graph.getEntry() && "Graph has no entry"); - } - - DataDependenceAnalysisImpl(ReadWriteGraph&& graph) - : DataDependenceAnalysisImpl(std::move(graph), {}) {} - - virtual ~DataDependenceAnalysisImpl() = default; - - ReadWriteGraph *getGraph() { return &graph; } - const ReadWriteGraph *getGraph() const { return &graph; } - const RWSubgraph *getEntry() const { return graph.getEntry(); } - // FIXME: rename to getEntryNode(); - //RWNode *getRoot() { return graph.getEntry()->getRoot(); } - const RWNode *getRoot() const { return graph.getEntry()->getRoot(); } - - virtual void run() = 0; - - // return the reaching definitions of ('mem', 'off', 'len') - // at the location 'where' - virtual std::vector - getDefinitions(RWNode *where, RWNode *mem, - const Offset& off, - const Offset& len) = 0; - - // return reaching definitions of a node that represents - // the given use - virtual std::vector getDefinitions(RWNode *use) = 0; -}; - -} // namespace dda -} // namespace dg - -#endif // DG_DATA_DEPENDENCE_ANALYSIS_IMPL_H_ diff --git a/include/dg/DataDependence/DataDependenceAnalysisOptions.h b/include/dg/DataDependence/DataDependenceAnalysisOptions.h deleted file mode 100644 index ab8333e6b..000000000 --- a/include/dg/DataDependence/DataDependenceAnalysisOptions.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef DG_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ -#define DG_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ - -#include - -#include "dg/Offset.h" -#include "dg/AnalysisOptions.h" - -namespace dg { - -struct FunctionModel { - std::string name; - - struct OperandValue { - enum class Type { - OFFSET, OPERAND - } type{Type::OFFSET}; - - union { - Offset offset; - unsigned operand; - } value{0}; - - bool isOffset() const { return type == Type::OFFSET; } - bool isOperand() const { return type == Type::OPERAND; } - Offset getOffset() const { assert(isOffset()); return value.offset; } - unsigned getOperand() const { assert(isOperand()); return value.operand; } - - OperandValue(Offset offset) : type(Type::OFFSET) { value.offset = offset; } - OperandValue(unsigned operand) : type(Type::OPERAND) { value.operand = operand; } - OperandValue(const OperandValue&) = default; - OperandValue(OperandValue&&) = default; - OperandValue& operator=(const OperandValue& rhs) { - type = rhs.type; - if (rhs.isOffset()) - value.offset = rhs.value.offset; - else - value.operand = rhs.value.operand; - return *this; - } - }; - - struct Operand { - unsigned operand; - OperandValue from, to; - - Operand(unsigned operand, OperandValue from, OperandValue to) - : operand(operand), from(from), to(to) {} - Operand(Operand&&) = default; - Operand(const Operand&) = default; - Operand& operator=(const Operand& rhs) { - operand = rhs.operand; - from = rhs.from; - to = rhs.to; - return *this; - } - }; - - void addDef(unsigned operand, OperandValue from, OperandValue to) { - _defines.emplace(operand, Operand{operand, from, to}); - } - - void addUse(unsigned operand, OperandValue from, OperandValue to) { - _uses.emplace(operand, Operand{operand, from, to}); - } - - void addDef(const Operand& op) { _defines.emplace(op.operand, op); } - void addUse(const Operand& op) { _uses.emplace(op.operand, op); } - - const Operand *defines(unsigned operand) const { - auto it = _defines.find(operand); - return it == _defines.end() ? nullptr : &it->second; - } - - const Operand *uses(unsigned operand) const { - auto it = _uses.find(operand); - return it == _uses.end() ? nullptr : &it->second; - } - - bool handles(unsigned i) const { - return defines(i) || uses(i); - } - -private: - std::map _defines; - std::map _uses; -}; - -namespace dda { -enum UndefinedFunsBehavior { - PURE = 0, - WRITE_ANY = 1, - READ_ANY = 1 << 1, - WRITE_ARGS = 1 << 2, - READ_ARGS = 1 << 3, -}; -} // namespace dda - - -struct DataDependenceAnalysisOptions : AnalysisOptions { - // default one - enum class AnalysisType { ssa } analysisType{AnalysisType::ssa}; - - bool isSSA() const { return analysisType == AnalysisType::ssa;} - - dda::UndefinedFunsBehavior undefinedFunsBehavior{dda::READ_ARGS}; - - // Does the analysis track concrete bytes - // or just objects? - bool fieldInsensitive{false}; - - bool undefinedArePure() const { return undefinedFunsBehavior == dda::PURE; } - bool undefinedFunsWriteAny() const { return undefinedFunsBehavior & dda::WRITE_ANY; } - bool undefinedFunsReadAny() const { return undefinedFunsBehavior & dda::READ_ANY; } - bool undefinedFunsWriteArgs() const { return undefinedFunsBehavior & dda::WRITE_ARGS; } - bool undefinedFunsReadArgs() const { return undefinedFunsBehavior & dda::READ_ARGS; } - - DataDependenceAnalysisOptions& setFieldInsensitive(bool b) { - fieldInsensitive = b; return *this; - } - - std::map functionModels; - - const FunctionModel *getFunctionModel(const std::string& name) const { - auto it = functionModels.find(name); - return it == functionModels.end() ? nullptr : &it->second; - } - - void functionModelAddDef(const std::string& name, const FunctionModel::Operand& def) { - auto& M = functionModels[name]; - if (M.name == "") - M.name = name; - M.addDef(def); - } - - void functionModelAddUse(const std::string& name, const FunctionModel::Operand& def) { - auto& M = functionModels[name]; - if (M.name == "") - M.name = name; - M.addUse(def); - } -}; - -} // namespace dg - -#endif // DG_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ diff --git a/include/dg/DependenceGraph.h b/include/dg/DependenceGraph.h deleted file mode 100644 index b2b265f1e..000000000 --- a/include/dg/DependenceGraph.h +++ /dev/null @@ -1,466 +0,0 @@ -#ifndef _DEPENDENCE_GRAPH_H_ -#define _DEPENDENCE_GRAPH_H_ - -#include -#include -#include -#include -#include - -#include "BBlock.h" -#include "ADT/DGContainer.h" -#include "Node.h" - -namespace dg { - -// non-template class representing a generic dependence graph -class DependenceGraphBase { - // how many nodes keeps pointer to this graph? - int refcount{1}; - - // is the graph in some slice? - uint64_t slice_id{0}; - -public: - virtual ~DependenceGraphBase() = default; - // dependence graph can be shared between more call-sites that - // has references to this graph. When destroying graph, we - // must be sure do delete it just once, so count references - // This is up to concrete DG implementation if it uses - // ref()/unref() methods or handle these stuff some other way - int ref() - { - ++refcount; - return refcount; - } - - // unref graph and delete if refrences drop to 0 - // destructor calls this on subgraphs - int unref(bool deleteOnZero = true) - { - --refcount; - - if (deleteOnZero && refcount == 0) { - delete this; - return 0; - } - - assert(refcount >= 0 && "Negative refcount"); - return refcount; - } - - // set that this graph (if it is subgraph) - // will be left in a slice. It is virtual, because the graph - // may want to override the function and take some action, - // if it is in a graph - // XXX: could we get rid of the virtual?? - virtual void setSlice(uint64_t sid) { - slice_id = sid; - } - - uint64_t getSlice() const { return slice_id; } -}; - -// ------------------------------------------------------------------- -// -- DependenceGraph -// -// This is a base template for a dependence graphs. Every concrete -// dependence graph will inherit from instance of this template. -// Dependece graph has a map of nodes that it contains (each node -// is required to have a unique key). Actually, there are two maps. -// One for nodes that are local to the graph and one for nodes that -// are global and can be shared between graphs. -// Concrete dependence graph may not use all attributes of this class -// and it is free to use them as it needs (e.g. it may use only -// global nodes and thus share them between all graphs) -// ------------------------------------------------------------------- -template -class DependenceGraph : public DependenceGraphBase { -public: - // type of key that is used in nodes - using KeyT = typename NodeT::KeyType; - // type of this dependence graph - so that we can refer to it in the code - using DependenceGraphT = typename NodeT::DependenceGraphType; - - using ContainerType = std::map; - using iterator = typename ContainerType::iterator; - using const_iterator = typename ContainerType::const_iterator; - using BBlocksMapT = std::map *>; - -private: - // entry and exit nodes of the graph - NodeT *entryNode; - NodeT *exitNode; - - // Formal parameters of the graph. Every graph is a graph of some function - // and formal parameters are parameters from its prototype, i. e. for - // foo(int a, int b) we have formal parameters 'a' and 'b'. Actual parameters - // are the values that are passed to function call, so for foo(3, x) - // the actual parameters are '3' and 'x'. - // Actual parameters are stored in the call node. - // Graph can have none or one formal parameters. - DGParameters *formalParameters; - - // call-sites (nodes) that are calling this graph - DGContainer callers; - - // blocks contained in this graph - BBlocksMapT _blocks; - - // if we want to keep CFG information in the dependence graph, - // these are entry and exit basic blocks - BBlock *entryBB; - BBlock *exitBB; - - // root of post-dominator tree - BBlock *PDTreeRoot; - -protected: - // nodes contained in this dg. They are protected, so that - // child classes can access them directly - ContainerType nodes; - // container that can be shared accross the graphs - // (therefore it is a pointer) - std::shared_ptr global_nodes; - -public: - DependenceGraph() - : entryNode(nullptr), exitNode(nullptr), formalParameters(nullptr) - , entryBB(nullptr), exitBB(nullptr), PDTreeRoot(nullptr) - { - } - - ~DependenceGraph() { -#ifdef ENABLE_DEBUG - bool deleted_entry = false; - bool deleted_exit = false; -#endif // ENABLE_DEBUG - for (auto& it : _blocks) { -#ifdef ENABLE_DEBUG - if (it.second == entryBB) - deleted_entry = true; - else if (it.second == exitBB); - deleted_exit = true; -#endif // ENABLE_DEBUG - delete it.second; - } - -#ifdef ENABLE_DEBUG - assert(deleted_entry && "Did not have entry in _blocks"); - assert(deleted_exit && "Did not have exit in _blocks"); -#endif // ENABLE_DEBUG - } - - // iterators for local nodes - iterator begin(void) { return nodes.begin(); } - const_iterator begin(void) const { return nodes.begin(); } - iterator end(void) { return nodes.end(); } - const_iterator end(void) const { return nodes.end(); } - - // operator [] for local nodes - NodeT *operator[](KeyT k) { return nodes[k]; } - const NodeT *operator[](KeyT k) const { return nodes[k]; } - - // reference getter for fast include-if-null operation - NodeT *& getRef(KeyT k) { return nodes[k]; } - - // do we have a local node with this key? - bool contains(KeyT k) const { return nodes.count(k) != 0; } - - // get iterator to a local node with key 'k'. If there is no - // such a node, return end() - iterator find(KeyT k) { return nodes.find(k); } - const_iterator find(KeyT k) const { return nodes.find(k); } - - // get formal parameters of this graph - DGParameters *getParameters() { return formalParameters;} - DGParameters *getParameters() const { return formalParameters;} - - // set new parameters of this graph. - void setParameters(DGParameters *p) - { - assert(!formalParameters && "Already have formal parameters"); - formalParameters = p; - } - - // Get node from graph for key. The function searches in nodes, - // formal parameters and global nodes (in this order) - // Return nullptr if no such node exists - template - NodeT *_getNode(T k) - { - auto it = nodes.find(k); - if (it != nodes.end()) - return it->second; - - if (formalParameters) { - auto p = formalParameters->find(k); - if (p) - return p->in; - } - - return getGlobalNode(k); - } - - NodeT *getNode(KeyT k) { return _getNode(k); } - const NodeT *getNode(const KeyT k) const { return _getNode(k); } - - // get global node with given key or null if there's - // not such node - template - NodeT *_getGlobalNode(T k) - { - if (global_nodes) { - auto it = global_nodes->find(k); - if (it != global_nodes->end()) - return it->second; - } - - return nullptr; - } - - NodeT *getGlobalNode(KeyT k) { return _getGlobalNode(k); } - const NodeT *getGlobalNode(const KeyT k) const { return _getGlobalNode(k); } - - // number of local nodes - size_t size() const - { - return nodes.size(); - } - - NodeT *setEntry(NodeT *n) - { - NodeT *oldEnt = entryNode; - entryNode = n; - - return oldEnt; - } - - NodeT *setExit(NodeT *n) - { - NodeT *oldExt = exitNode; - exitNode = n; - - return oldExt; - } - - NodeT *getEntry(void) const { return entryNode; } - NodeT *getExit(void) const { return exitNode; } - - void setGlobalNodes(const std::shared_ptr& ngn) - { - global_nodes = ngn; - } - - // allocate new global nodes - void allocateGlobalNodes() - { - assert(!global_nodes && "Already contains global nodes"); - // std::make_shared returned unaligned pointer for some reason... - global_nodes = std::shared_ptr(new ContainerType()); - } - - ContainerType *getNodes() - { - return &nodes; - } - - const ContainerType *getNodes() const - { - return &nodes; - } - - std::shared_ptr getGlobalNodes() - { - return global_nodes; - } - - const std::shared_ptr& getGlobalNodes() const - { - return global_nodes; - } - - // add a node to this graph. The DependenceGraph is something like - // a namespace for nodes, since every node has unique key and we can - // have another node with same key in another graph. - // So we can have two nodes for the same value but in different - // graphs. The edges can be between arbitrary nodes and do not - // depend on graphs the nodes are in. - bool addNode(KeyT k, NodeT *n) - { - bool ret = nodes.insert(std::make_pair(k, n)).second; - if (ret) { - assert(n->getDG() == nullptr && "A node can not belong to more graphs"); - n->setDG(static_cast(this)); - } - - return ret; - } - - // make it virtual? We don't need it now, but - // in the future it may be handy. - bool addNode(NodeT *n) - { - // NodeT is a class derived from - // dg::Node, so it must have getKey() method - return addNode(n->getKey(), n); - } - - bool addGlobalNode(KeyT k, NodeT *n) - { - assert(global_nodes && "Need a container for global nodes first"); - return global_nodes->insert(std::make_pair(k, n)).second; - } - - bool addGlobalNode(NodeT *n) - { - return addGlobalNode(n->getKey(), n); - } - - NodeT *removeNode(KeyT k) - { - return _removeNode(k, &nodes); - } - - NodeT *removeNode(NodeT *n) - { - return removeNode(n->getKey()); - } - - NodeT *removeNode(iterator& it) - { - return _removeNode(it, &nodes); - } - - NodeT *removeGlobalNode(KeyT k) - { - if (!global_nodes) - return nullptr; - - return _removeNode(k, global_nodes); - } - - NodeT *removeGlobalNode(NodeT *n) - { - return removeGlobalNode(n->getKey()); - } - - NodeT *removeGlobalNode(iterator& it) - { - if (!global_nodes) - return nullptr; - - return _removeNode(it, global_nodes); - } - - bool deleteNode(NodeT *n) - { - return deleteNode(n->getKey()); - } - - bool deleteNode(KeyT k) - { - NodeT *n = removeNode(k); - delete n; - - return n != nullptr; - } - - bool deleteNode(iterator& it) - { - NodeT *n = removeNode(it); - delete n; - - return n != nullptr; - } - - bool deleteGlobalNode(KeyT k) - { - NodeT *n = removeGlobalNode(k); - delete n; - - return n != nullptr; - } - - bool deleteGlobalNode(NodeT *n) - { - return deleteGlobalNode(n->getKey()); - } - - bool deleteGlobalNode(iterator& it) - { - NodeT *n = removeGlobalNode(it); - delete n; - - return n != nullptr; - } - - DGContainer& getCallers() { return callers; } - const DGContainer& getCallers() const { return callers; } - bool addCaller(NodeT *sg) { return callers.insert(sg); } - - // get blocks contained in this graph - BBlocksMapT& getBlocks() { return _blocks; } - const BBlocksMapT& getBlocks() const { return _blocks; } - // add block to this graph - bool addBlock(KeyT key, BBlock *B) { - return _blocks.emplace(key, B).second; - } - - bool removeBlock(KeyT key) - { - return _blocks.erase(key) == 1; - } - - BBlock *getPostDominatorTreeRoot() const { return PDTreeRoot; } - void setPostDominatorTreeRoot(BBlock *r) - { - assert(!PDTreeRoot && "Already has a post-dominator tree root"); - PDTreeRoot = r; - } - - BBlock *getEntryBB() const { return entryBB; } - BBlock *getExitBB() const { return exitBB; } - - BBlock *setEntryBB(BBlock *nbb) - { - BBlock *old = entryBB; - entryBB = nbb; - - return old; - } - - BBlock *setExitBB(BBlock *nbb) - { - BBlock *old = exitBB; - exitBB = nbb; - - return old; - } - - -private: - - NodeT *_removeNode(iterator& it, ContainerType *cont) - { - NodeT *n = it->second; - n->isolate(); - cont->erase(it); - - return n; - } - - NodeT *_removeNode(KeyT k, ContainerType *cont) - { - iterator it = cont->find(k); - if (it == cont->end()) - return nullptr; - - // remove and re-connect edges - return _removeNode(it, cont); - } -}; - -} // namespace dg - -#endif // _DEPENDENCE_GRAPH_H_ diff --git a/include/dg/Dominators/DominanceFrontiers.h b/include/dg/Dominators/DominanceFrontiers.h deleted file mode 100644 index 6b1f66ac4..000000000 --- a/include/dg/Dominators/DominanceFrontiers.h +++ /dev/null @@ -1,75 +0,0 @@ -#ifndef DG_DOMINANCE_FRONTIERS_H_ -#define DG_DOMINANCE_FRONTIERS_H_ - -#include - -#include "BBlock.h" -#include "BFS.h" - -namespace dg { - -/// -// Compute dominance frontiers -// -// \param root root of dominators tree -// -// This algorithm takes dominator tree -// (edges of the tree are in BBlocks) and computes -// dominance frontiers for every node -// -// The algorithm is due: -// -// R. Cytron, J. Ferrante, B. K. Rosen, M. N. Wegman, and F. K. Zadeck. 1989. -// An efficient method of computing static single assignment form. -// In Proceedings of the 16th ACM SIGPLAN-SIGACT symposium on Principles of programming languages (POPL '89), -// CORPORATE New York, NY Association for Computing Machinery (Ed.). ACM, New York, NY, USA, 25-35. -// DOI=http://dx.doi.org/10.1145/75277.75280 -// -template -class DominanceFrontiers -{ - static void queueDomBBs(BBlock *BB, - std::vector *> *blocks) - { - blocks->push_back(BB); - } - - void computeDFrontiers(BBlock *X) - { - // DF_local - for (const auto& edge : X->successors()) { - BBlock *Y = edge.target; - if (Y->getIDom() != X) { - X->addDomFrontier(Y); - } - } - - // DF_up - for (BBlock *Z : X->getDominators()) { - for (BBlock *Y : Z->getDomFrontiers()) { - if (Y->getIDom() != X) { - X->addDomFrontier(Y); - } - } - } - } - -public: - void compute(BBlock *root) - { - std::vector *> blocks; - BBlockBFS bfs(BFS_BB_DOM); - - // get BBs in the order of dom tree edges (BFS), - // so that we process it bottom-up - bfs.run(root, queueDomBBs, &blocks); - - // go bottom-up the dom tree and compute domninance frontiers - for (int i = blocks.size() - 1; i >= 0; --i) - computeDFrontiers(blocks[i]); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/Dominators/PostDominanceFrontiers.h b/include/dg/Dominators/PostDominanceFrontiers.h deleted file mode 100644 index 9dcdd6f9b..000000000 --- a/include/dg/Dominators/PostDominanceFrontiers.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef DG_POST_DOMINANCE_FRONTIERS_H_ -#define DG_POST_DOMINANCE_FRONTIERS_H_ - -#include - -#include "dg/BBlock.h" -#include "dg/BFS.h" - -namespace dg { -namespace legacy { - -/// -// Compute post-dominance frontiers -// -// \param root root of post-dominators tree -// -// This algorithm takes post-dominator tree -// (edges of the tree are in BBlocks) and computes -// post-dominator frontiers for every node -// -// The algorithm is due: -// -// R. Cytron, J. Ferrante, B. K. Rosen, M. N. Wegman, and F. K. Zadeck. 1989. -// An efficient method of computing static single assignment form. -// In Proceedings of the 16th ACM SIGPLAN-SIGACT symposium on Principles of programming languages (POPL '89), -// CORPORATE New York, NY Association for Computing Machinery (Ed.). ACM, New York, NY, USA, 25-35. -// DOI=http://dx.doi.org/10.1145/75277.75280 -// -template -class PostDominanceFrontiers { - - void computePDFrontiers(BBlockT *BB, bool add_cd) { - // compute DFlocal - for (auto *pred : BB->predecessors()) { - auto *ipdom = pred->getIPostDom(); - if (ipdom && ipdom != BB) { - BB->addPostDomFrontier(pred); - - // pd-frontiers are the reverse control dependencies - if (add_cd) - pred->addControlDependence(BB); - } - } - - for (auto *pdom : BB->getPostDominators()) { - for (auto *df : pdom->getPostDomFrontiers()) { - auto *ipdom = df->getIPostDom(); - if (ipdom && ipdom != BB && df != BB) { - BB->addPostDomFrontier(df); - - if (add_cd) - df->addControlDependence(BB); - } - } - } - } - -public: - void compute(BBlockT *root, bool add_cd = false) - { - std::vector blocks; - - struct EdgeChooser { - class range { - BBlockT *_blk; - public: - range(BBlockT *blk) : _blk(blk) {} - - auto begin() -> decltype(_blk->getPostDominators().begin()) { - return _blk->getPostDominators().begin(); - } - - auto end() -> decltype(_blk->getPostDominators().end()) { - return _blk->getPostDominators().end(); - } - }; - - range operator()(BBlockT *b) const { return range(b); } - }; - - EdgeChooser chooser; - BFS, EdgeChooser> bfs(chooser); - - // get BBs in the order of post-dom tree edges (BFS), - // so that we process it bottom-up - bfs.run(root, [&blocks](BBlockT *b) { blocks.push_back(b); }); - - // go bottom-up the post-dom tree and compute post-domninance frontiers - for (int i = blocks.size() - 1; i >= 0; --i) - computePDFrontiers(blocks[i], add_cd); - } -}; - -} // namespace legacy -} // namespace dg - -#endif diff --git a/include/dg/MemorySSA/Definitions.h b/include/dg/MemorySSA/Definitions.h deleted file mode 100644 index f4ec423b7..000000000 --- a/include/dg/MemorySSA/Definitions.h +++ /dev/null @@ -1,94 +0,0 @@ -#ifndef DG_MEMORY_SSA_DEFINITIONS_H_ -#define DG_MEMORY_SSA_DEFINITIONS_H_ - -#include -#include - -#include "dg/Offset.h" -#include "dg/MemorySSA/DefinitionsMap.h" -#include "dg/ReadWriteGraph/RWNode.h" - -namespace dg { -namespace dda { - -// information about definitions associated to each bblock -struct Definitions { - bool _processed{false}; - - // definitions gathered at the end of this bblock - // (if you find the sought memory here, - // you got all definitions from this block) - DefinitionsMap definitions; - // all memory that is overwritten by this block (strong update) - // FIXME: we should have just a mapping from memory to disjunctive intervals - // as data structure here (if you find the sought memory here, you can - // terminate the search) - DefinitionsMap kills; - - // writes to unknown memory in this block - std::vector unknownWrites; - // just a cache - std::vector unknownReads; - - void swap(Definitions& rhs) { - definitions.swap(rhs.definitions); - kills.swap(rhs.kills); - unknownWrites.swap(rhs.unknownWrites); - unknownReads.swap(rhs.unknownReads); - } - - void addUnknownWrite(RWNode *n) { - unknownWrites.push_back(n); - } - - void addUnknownRead(RWNode *n) { - unknownReads.push_back(n); - } - - const std::vector& getUnknownWrites() const { - return unknownWrites; - } - - const std::vector& getUnknownReads() const { - return unknownReads; - } - - /// - /// get the definition-sites for the given 'ds' - /// - std::set get(const DefSite& ds) { - auto retval = definitions.get(ds); - if (retval.empty()) { - retval.insert(unknownWrites.begin(), unknownWrites.end()); - } - return retval; - } - - // update this Definitions by definitions from 'node'. - // I.e., as if node would be executed when already - // having the definitions we have - void update(RWNode *node, RWNode *defnode = nullptr); - // Join another definitions to this Definitions - // (as if on a joint point in a CFG) - void join(const Definitions& rhs); - - auto uncovered(const DefSite& ds) const -> decltype(kills.undefinedIntervals(ds)) { - return kills.undefinedIntervals(ds); - } - - // for on-demand analysis - // once isProcessed is true, the Defiitions contain - // summarized all the information that one needs - // (may be then altered just only by adding a phi node definitions) - bool isProcessed() const { return _processed; } - void setProcessed() { _processed = true; } - -#ifndef NDEBUG - void dump() const; -#endif -}; - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/MemorySSA/DefinitionsMap.h b/include/dg/MemorySSA/DefinitionsMap.h deleted file mode 100644 index 3f92c668a..000000000 --- a/include/dg/MemorySSA/DefinitionsMap.h +++ /dev/null @@ -1,209 +0,0 @@ -#ifndef DG_DEFINITIONS_MAP_H_ -#define DG_DEFINITIONS_MAP_H_ - -#include -#include -#include -#ifndef NDEBUG -#include -#endif - -#include "dg/Offset.h" -#include "dg/ReadWriteGraph/DefSite.h" -#include "dg/ADT/DisjunctiveIntervalMap.h" - -namespace dg { -namespace dda { - -class RWNode; -class ReachingDefinitionsAnalysis; - -/// A data structure that represents a mapping -/// DefSite -> RWNode, that is, it stores which memory (DefSite) -/// was defined where. -template -class DefinitionsMap { -public: - using OffsetsT = ADT::DisjunctiveIntervalMap; - using IntervalT = typename OffsetsT::IntervalT; - -private: - std::unordered_map _definitions{}; - - // transform (offset, lenght) from a DefSite into the interval - static std::pair getInterval(const DefSite& ds) { - // if the offset is unknown, stretch the interval over all possible bytes - if (ds.offset.isUnknown()) - return {0, Offset::UNKNOWN}; - - return {ds.offset, ds.offset + (ds.len - 1)}; - } - -public: - void clear() { _definitions.clear(); } - void swap(DefinitionsMap& rhs) { _definitions.swap(rhs._definitions); } - bool empty() const { return _definitions.empty(); } - - bool add(const DefSite& ds, NodeT *node) { - // if the offset is unknown, make it 0, so that the - // definition get stretched over all possible offsets - Offset start, end; - std::tie(start, end) = getInterval(ds); - return _definitions[ds.target].add(start, end, node); - } - - bool addAll(NodeT *node) { - bool changed = false; - for (auto& it : _definitions) { - changed |= it.second.addAll(node); - } - return changed; - } - - bool update(const DefSite& ds, NodeT *node) { - Offset start, end; - std::tie(start, end) = getInterval(ds); - return _definitions[ds.target].update(start, end, node); - } - - template - bool add(const DefSite& ds, const ContainerT& nodes) { - bool changed = false; - for (auto n : nodes) - changed |= add(ds, n); - return changed; - } - - template - bool add(const ContainerT& defsites, NodeT *n) { - bool changed = false; - for (auto& ds : defsites) - changed |= add(ds, n); - return changed; - } - - bool add(NodeT *target, const OffsetsT& elems) { - bool changed = false; - for (auto& it : elems) - changed |= _definitions[target].add(it.first, it.second); - return changed; - } - - bool add(const DefinitionsMap& rhs) { - bool changed = false; - for (auto& it : rhs){ - changed |= add(it.first, it.second); - } - return changed; - } - - bool update(const DefSite& ds, const std::vector& nodes) { - bool changed = false; - for (auto n : nodes) - changed |= update(ds, n); - return changed; - } - - /// - // Get definitions of the memory described by 'ds' - std::set get(const DefSite& ds) { - auto it = _definitions.find(ds.target); - if (it == _definitions.end()) - return {}; - - Offset start, end; - std::tie(start, end) = getInterval(ds); - return it->second.gather(start, end); - } - - /// - // Return intervals of bytes from 'ds' that are not defined by this map - std::vector undefinedIntervals(const DefSite& ds) const { - auto it = _definitions.find(ds.target); - if (it == _definitions.end()) - return {IntervalT(ds.offset, ds.offset + (ds.len - 1))}; - - Offset start, end; - std::tie(start, end) = getInterval(ds); - return it->second.uncovered(start, end); - } - - bool definesTarget(NodeT *target) const { - return _definitions.find(target) != _definitions.end(); - } - - /* - template - DefinitionsMap filter(KeyFilt keyfilt, SetFilt setfilt) { - DefinitionsMap tmp; - for (auto& it : _definitions) { - if (keyfilt(it.first) && setfilt(it.second)) { - tmp._definitions.emplace(it.first, it.second); - } - } - return tmp; - } - */ - - template - DefinitionsMap filter(FiltFun filt) { - DefinitionsMap tmp; - for (auto& it : _definitions) { - if (filt(it.first)) { - tmp._definitions.emplace(it.first, it.second); - } - } - return tmp; - } - - DefinitionsMap intersect(const DefinitionsMap& rhs) { - DefinitionsMap retval; - for (auto& it : _definitions) { - auto rhsit = rhs._definitions.find(it.first); - if (rhsit != rhs._definitions.end()) { - retval.add(it.first, it.second.intersection(rhsit->second)); - } - } - return retval; - } - - // FIXME: do that as iterators - std::set values() const { - std::set ret; - for (auto& it : _definitions) { - for (auto& it2: it.second) { - ret.insert(it2.second.begin(), it2.second.end()); - } - } - return ret; - } - - auto begin() const -> decltype(_definitions.begin()) { - return _definitions.begin(); - } - - auto end() const -> decltype(_definitions.end()) { - return _definitions.end(); - } - - bool operator==(const DefinitionsMap& oth) const { - return _definitions == oth._definitions; - } - - size_t size() const { return _definitions.size(); } - -#ifndef NDEBUG - void dump() const { - for (auto& it : _definitions) { - it.first->dump(); - std::cout << " defined at "; - it.second.dump(); - } - } -#endif -}; - -} // dda -} // dg - -#endif diff --git a/include/dg/MemorySSA/MemorySSA.h b/include/dg/MemorySSA/MemorySSA.h deleted file mode 100644 index db4f16315..000000000 --- a/include/dg/MemorySSA/MemorySSA.h +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef DG_MEMORY_SSA_H_ -#define DG_MEMORY_SSA_H_ - -#include -#include -#include -#include - -#include "dg/Offset.h" - -#include "dg/DataDependence/DataDependenceAnalysisOptions.h" -#include "dg/DataDependence/DataDependenceAnalysisImpl.h" -#include "dg/MemorySSA/DefinitionsMap.h" - -#include "dg/ReadWriteGraph/ReadWriteGraph.h" - -#include "dg/ADT/Queue.h" -#include "dg/util/debug.h" - -#include "Definitions.h" -#include "ModRef.h" - -namespace dg { -namespace dda { - -class MemorySSATransformation : public DataDependenceAnalysisImpl { - - class BBlockInfo { - Definitions definitions{}; - RWNodeCall *call{nullptr}; - - public: - void setCallBlock(RWNodeCall *c) { call = c; } - bool isCallBlock() const { return call != nullptr; } - RWNodeCall *getCall() { return call; } - const RWNodeCall *getCall() const { return call; } - - Definitions& getDefinitions() { return definitions; } - const Definitions& getDefinitions() const { return definitions; } - }; - - class SubgraphInfo { - std::unordered_map _bblock_infos; - - class Summary { - public: - // phi nodes representing reads/writes to memory that is - // external to the procedure - DefinitionsMap inputs; - DefinitionsMap outputs; - - Summary() = default; - Summary(Summary&&) = default; - Summary(const Summary&) = delete; - - void addInput(const DefSite& ds, RWNode *n) { inputs.add(ds, n); } - void addOutput(const DefSite& ds, RWNode *n) { outputs.add(ds, n); } - - RWNode *getUnknownPhi() { - // FIXME: optimize this, we create std::set for nothing... - auto S = inputs.get({UNKNOWN_MEMORY, 0, Offset::UNKNOWN}); - if (S.empty()) { - return nullptr; - } - assert(S.size() == 1); - return *(S.begin()); - } - - std::set getOutputs(const DefSite& ds) { return outputs.get(ds); } - auto getUncoveredOutputs(const DefSite& ds) -> decltype (outputs.undefinedIntervals(ds)) { - return outputs.undefinedIntervals(ds); - } - } summary; - - // sumarized information about visible external - // effects of the procedure - ModRefInfo modref; - - SubgraphInfo(RWSubgraph *s); - - friend class MemorySSATransformation; - - public: - SubgraphInfo() = default; - - Summary& getSummary() { return summary; } - const Summary& getSummary() const { return summary; } - BBlockInfo& getBBlockInfo(RWBBlock *b) { return _bblock_infos[b]; } - const BBlockInfo *getBBlockInfo(RWBBlock *b) const { - auto it = _bblock_infos.find(b); - return it == _bblock_infos.end() ? nullptr : &it->second; - } - }; - - void initialize(); - - //// - // LVN - /// - // Perform LVN up to a certain point and search only for a certain memory. - // XXX: we could avoid this by (at least virtually) splitting blocks on uses. - Definitions findDefinitionsInBlock(RWNode *to, const RWNode *mem = nullptr); - Definitions findEscapingDefinitionsInBlock(RWNode *to); - void performLvn(Definitions&, RWBBlock *); - void updateDefinitions(Definitions& D, RWNode *node); - - /// - // Find definitions of the def site and return def-use edges. - // For the uncovered bytes create phi nodes (which are also returned - // as the definitions). - std::vector findDefinitions(RWBBlock *, const DefSite&); - std::vector findDefinitions(RWNode *node, const DefSite& ds); - - // Find definitions for the given node (which is supposed to be a use) - std::vector findDefinitions(RWNode *node); - - std::vector findDefinitionsInPredecessors(RWBBlock *block, - const DefSite& ds); - - void findDefinitionsInMultiplePredecessors(RWBBlock *block, - const DefSite& ds, - std::vector& defs); - - void addUncoveredFromPredecessors(RWBBlock *block, - Definitions& D, - const DefSite& ds, - std::vector& defs); - - void findPhiDefinitions(RWNode *phi); - - /// - // Search call C for definitions of ds and store the results into D. - // Used to implement on-demand search inside procedures. - void fillDefinitionsFromCall(Definitions& D, RWNodeCall *C, const DefSite& ds); - /// - // Search call C for all definitions that may be visible after the call. - // After the call to this method, D is completely filled with all - // information, similarly as when we perform LVN for non-call bblock. - void fillDefinitionsFromCall(Definitions& D, RWNodeCall *C); - - void findDefinitionsFromCalledFun(RWNode *phi, RWSubgraph *subg, const DefSite& ds); - - void addDefsFromUndefCall(Definitions& D, RWNode *defs, - RWNode *call, bool isstrong); - - template - void findPhiDefinitions(RWNode *phi, Iterable& I) { - std::set defs; - - assert(phi->getOverwrites().size() == 1); - const auto& ds = *(phi->getOverwrites().begin()); - // we handle this case separately - assert(!ds.target->isUnknown() && "PHI for unknown memory"); - - for (auto *block : I) { - auto tmpdefs = findDefinitions(block, ds); - defs.insert(tmpdefs.begin(), tmpdefs.end()); - } - - phi->addDefUse(defs); - } - - /// Finding definitions for unknown memory - // Must be called after LVN proceeded - ideally only when the client is getting the definitions - std::vector findAllDefinitions(RWNode *from); - Definitions collectAllDefinitions(RWNode *from); - /// if escaping is set to true, collect only definitions of escaping memory - // (optimization for searching definitions in callers) - void collectAllDefinitions(RWNode *from, Definitions& defs, bool esacping = false); - void collectAllDefinitions(Definitions& defs, - RWBBlock *from, - std::set& visitedBlocks, - bool escaping); - - void collectAllDefinitionsInCallers(Definitions& defs, RWSubgraph *subg); - - void findDefinitionsInSubgraph(RWNode *phi, - RWNodeCall *C, - const DefSite& ds, - RWSubgraph *subg); - - void addDefinitionsFromCalledValue(RWNode *phi, - RWNodeCall *C, - const DefSite& ds, - RWNode *calledValue); - - void computeModRef(RWSubgraph *subg, SubgraphInfo& si); - bool callMayDefineTarget(RWNodeCall *C, RWNode *target); - - RWNode *createPhi(const DefSite& ds, RWNodeType type = RWNodeType::PHI); - RWNode *createPhi(Definitions& D, const DefSite& ds, RWNodeType type = RWNodeType::PHI); - RWNode *createAndPlacePhi(RWBBlock *block, const DefSite& ds); - - // insert a (temporary) use into the graph before the node 'where' - RWNode *insertUse(RWNode *where, RWNode *mem, - const Offset& off, const Offset& len); - - std::vector _phis; - dg::ADT::QueueLIFO _queue; - std::unordered_map _subgraphs_info; - - Definitions& getBBlockDefinitions(RWBBlock *b, const DefSite *ds = nullptr); - - SubgraphInfo& getSubgraphInfo(const RWSubgraph *s) { return _subgraphs_info[s]; } - const SubgraphInfo *getSubgraphInfo(const RWSubgraph *s) const { - auto it = _subgraphs_info.find(s); - return it == _subgraphs_info.end() ? nullptr : &it->second; - } - BBlockInfo& getBBlockInfo(RWBBlock *b) { - return getSubgraphInfo(b->getSubgraph()).getBBlockInfo(b); - } - - const BBlockInfo *getBBlockInfo(RWBBlock *b) const { - auto *si = getSubgraphInfo(b->getSubgraph()); - if (si) { - return si->getBBlockInfo(b); - } - return nullptr; - } - - SubgraphInfo::Summary& getSubgraphSummary(const RWSubgraph *s) { - return getSubgraphInfo(s).getSummary(); - } - -public: - MemorySSATransformation(ReadWriteGraph&& graph, - const DataDependenceAnalysisOptions& opts) - : DataDependenceAnalysisImpl(std::move(graph), opts) {} - - MemorySSATransformation(ReadWriteGraph&& graph) - : DataDependenceAnalysisImpl(std::move(graph)) {} - - void run() override; - - // compute definitions for all uses at once - // (otherwise the definitions are computed on demand - // when calling getDefinitions()) - void computeAllDefinitions(); - - // return the reaching definitions of ('mem', 'off', 'len') - // at the location 'where' - std::vector getDefinitions(RWNode *where, - RWNode *mem, - const Offset& off, - const Offset& len) override; - - std::vector getDefinitions(RWNode *use) override; - - const Definitions *getDefinitions(RWBBlock *b) const { - auto *bi = getBBlockInfo(b); - return bi ? &bi->getDefinitions() : nullptr; - } - - const SubgraphInfo::Summary *getSummary(const RWSubgraph *s) const { - auto si = getSubgraphInfo(s); - if (!si) - return nullptr; - return &si->getSummary(); - } -}; - -} // namespace dda -} // namespace dg - -#endif // DG_MEMORY_SSA_H_ diff --git a/include/dg/MemorySSA/ModRef.h b/include/dg/MemorySSA/ModRef.h deleted file mode 100644 index 8433f967c..000000000 --- a/include/dg/MemorySSA/ModRef.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef DG_MOD_REF_H_ -#define DG_MOD_REF_H_ - -#include "dg/Offset.h" - -#include "dg/MemorySSA/DefinitionsMap.h" -#include "dg/ReadWriteGraph/RWNode.h" - - -namespace dg { -namespace dda { - -// sumarized information about visible external -// effects of the procedure -class ModRefInfo { - // to distinguish between empty and non-computed modref information - bool _initialized{false}; -public: - // the set of memory that is defined in this procedure - // and is external to the subgraph or is local but its address is taken - // In other words, memory whose definitions can be "visible" - // outside the procedure. - // FIXME: we should keep only sets of DefSites - DefinitionsMap maydef; - // external or local address-taken memory that can be - // used inside the procedure - // FIXME: we should keep only sets of DefSites - DefinitionsMap mayref; - // memory that must be defined in this procedure - // (on every path through the procedure) - DefinitionsMap mustdef; - - void addMayDef(const DefSite& ds, RWNode *def) { - // FIXME: do not store def, it is useless. Just takes memory... - maydef.add(ds, def); - } - - template - void addMayDef(const C& c, RWNode *def) { - for (auto& ds : c) { - maydef.add(ds, def); - } - } - - void addMayRef(const DefSite& ds, RWNode *ref) { - // FIXME: do not store ref, it is useless. Just takes memory... - mayref.add(ds, ref); - } - - template - void addMayRef(const C& c, RWNode *ref) { - for (auto& ds : c) { - mayref.add(ds, ref); - } - } - - void addMustDef(const DefSite& ds, RWNode *def) { - // FIXME: do not store def, it is useless. Just takes memory... - mustdef.add(ds, def); - } - - template - void addMustDef(const C& c, RWNode *def) { - for (auto& ds : c) { - mustdef.add(ds, def); - } - } - - void add(const ModRefInfo& oth) { - maydef.add(oth.maydef); - mayref.add(oth.mayref); - mustdef.add(oth.mustdef); - } - - /// - // Check whether the procedure may define 'n' (ignoring writes - // to unknown memory, \see mayDefineOrUnknown()) - bool mayDefine(RWNode *n) const { return maydef.definesTarget(n); } - bool mayDefineUnknown() const { return mayDefine(UNKNOWN_MEMORY); } - - /// - // Check whether the procedure may define 'n', taking into - // account also writes to unknown memory - bool mayDefineOrUnknown(RWNode *n) const { - return mayDefine(n) or mayDefineUnknown(); - } - - auto getMayDef(RWNode *n) -> decltype(maydef.get(n)) { - return maydef.get(n); - } - - void setInitialized() { _initialized = true; } - bool isInitialized() const { return _initialized ; } -}; - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/MemoryState.h b/include/dg/MemoryState.h deleted file mode 100644 index be845b3a2..000000000 --- a/include/dg/MemoryState.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef DG_MEMORY_STATE_H_ -#define DG_MEMORY_STATE_H_ - -#include -#include "dg/util/cow_shared_ptr.h" - -namespace dg { - -// Representation of memory state with copy-on-write support -template -class MemoryState { -public: - using Map = std::map>; - - const Object *get(Key k) const { - auto it = _memory.find(k); - if (it == _memory.end()) - return nullptr; - return it->second.get(); - }; - - Object *getWritable(Key k) { - return _memory[k].getWritable(); - }; - - void put(Key k, Object *o) { - _memory[k].reset(o); - } - - // take the memory state rhs and copy - // entries for which this state does not have entry - bool copyMissing(const MemoryState& rhs) { - bool changed = false; - for (const auto& rit : rhs._memory) { - auto it = _memory.find(rit.first); - if (it == _memory.end()) { - _memory.emplace_hint(it, rit); - changed = true; - } - } - - return changed; - } - - bool merge(const MemoryState& rhs) { - bool changed = false; - for (const auto& rit : rhs._memory) { - auto it = _memory.find(rit.first); - if (it == _memory.end()) { - _memory.emplace_hint(it, rit); - changed = true; - } else { - // no update necessary - if (it->second == rit.second) - continue; - auto our = it->second.getWritable(); - changed |= our->merge(*rit.second.get()); - } - } - - return changed; - } - -private: - Map _memory; - -public: - - auto begin() -> decltype(_memory.begin()) { return _memory.begin(); } - auto end() -> decltype(_memory.end()) { return _memory.end(); } - auto begin() const -> decltype(_memory.begin()) { return _memory.begin(); } - auto end() const -> decltype(_memory.end()) { return _memory.end(); } - - // FIXME: add proper iterator -}; - -// copy-on-write container around MemoryState -template -class COWMemoryState { - cow_shared_ptr> state; - - const Object *get(Key k) const { return state->get(k); }; - - Object *getWritable(Key k) { - return state.getWritable()->getWritable(k); - }; - - void put(Key k, Object *o) { - state.getWritable()->put(k, o); - } - - // take the memory state rhs and copy - // entries for which this state does not have entry - bool copyMissing(const MemoryState& rhs) { - return state.getWritable()->copyMissing(rhs); - } - - bool copyMissing(const COWMemoryState& rhs) { - return state.getWritable()->copyMissing(rhs.state); - } - - bool merge(const MemoryState& rhs) { - return state.getWritable()->merge(rhs); - } - - bool merge(const COWMemoryState& rhs) { - return state.getWritable()->merge(rhs.state); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/Node.h b/include/dg/Node.h deleted file mode 100644 index c9b1c26ee..000000000 --- a/include/dg/Node.h +++ /dev/null @@ -1,425 +0,0 @@ -#ifndef NODE_H_ -#define NODE_H_ - -#include "DGParameters.h" -#include "ADT/DGContainer.h" -#include "legacy/Analysis.h" - -namespace dg { - -template -class DependenceGraph; - -/// ------------------------------------------------------------------ -// -- Node -// one node in DependenceGraph. The type of dependence graph is -// fully determined by the type of node. Dependence graph is just -// a container for nodes - everything interesting is here. -// Concrete implementation will inherit from an instance of this -// template. -/// ------------------------------------------------------------------ -template -class Node -{ -public: - using EdgesT = EdgesContainer; - using ControlEdgesT = EdgesT; - using DataEdgesT = EdgesT; - using UseEdgesT = EdgesT; - using InterferenceEdges = EdgesT; - - // to be able to reference the KeyT and DG - using KeyType = KeyT; - using DependenceGraphType = DependenceGraphT; - - using control_iterator = typename ControlEdgesT::iterator; - using const_control_iterator = typename ControlEdgesT::const_iterator; - using data_iterator = typename DataEdgesT::iterator; - using const_data_iterator = typename DataEdgesT::const_iterator; - using use_iterator = typename DataEdgesT::iterator; - using const_use_iterator = typename DataEdgesT::const_iterator; - using interference_iterator = typename InterferenceEdges::iterator; - using const_interference_iterator = typename InterferenceEdges::const_iterator; - - Node(const KeyT& k) : key(k) {} - - DependenceGraphT *setDG(DependenceGraphT *dg) - { - DependenceGraphT *old = this->dg; - this->dg = dg; - - return old; - } - - DependenceGraphT *getDG() const - { - return dg; - } - - // add control dependence edge 'this'-->'n', - // thus making 'n' control dependend on this node - bool addControlDependence(NodeT *n) - { - return _addBidirectionalEdge(static_cast(this), n, - controlDepEdges, n->revControlDepEdges); - } - - // add data dependence edge 'this'-->'n', - // thus making 'n' data dependend on this node - bool addDataDependence(NodeT *n) - { - return _addBidirectionalEdge(static_cast(this), n, - dataDepEdges, n->revDataDepEdges); - } - - // this node uses (e.g. like an operand) the node 'n' - bool addUseDependence(NodeT *n) - { - return _addBidirectionalEdge(static_cast(this), n, - useEdges, n->userEdges); - } - - bool addInterferenceDependence(NodeT *n) - { - return _addBidirectionalEdge(static_cast(this), n, - interferenceDepEdges, n->revInterferenceDepEdges); - } - - // remove edge 'this'-->'n' from control dependencies - bool removeControlDependence(NodeT *n) - { - return _removeBidirectionalEdge(static_cast(this), n, - controlDepEdges, n->revControlDepEdges); - } - - // remove edge 'this'-->'n' from data dependencies - bool removeDataDependence(NodeT *n) - { - return _removeBidirectionalEdge(static_cast(this), n, - dataDepEdges, n->revDataDepEdges); - } - - - bool removeUseDependence(NodeT * n) - { - return _removeBidirectionalEdge(static_cast(this), n, - useEdges, n->userEdges); - } - - bool removeInterferenceDependence(NodeT *n) - { - return _removeBidirectionalEdge(static_cast(this), n, - interferenceDepEdges, n->revInterferenceDepEdges); - } - - // remove all control dependencies going from/to this node - void removeOutcomingCDs() - { - while (!controlDepEdges.empty()) - removeControlDependence(*controlDepEdges.begin()); - } - - void removeIncomingCDs() - { - while (!revControlDepEdges.empty()) { - NodeT *cd = *revControlDepEdges.begin(); - // this will remove the reverse control dependence from - // this node - cd->removeControlDependence(static_cast(this)); - } - } - - void removeCDs() - { - removeOutcomingCDs(); - removeIncomingCDs(); - } - - void removeOutcomingDDs() - { - while (!dataDepEdges.empty()) - removeDataDependence(*dataDepEdges.begin()); - } - - void removeIncomingDDs() - { - while (!revDataDepEdges.empty()) { - NodeT *cd = *revDataDepEdges.begin(); - // this will remove the reverse control dependence from - // this node - cd->removeDataDependence(static_cast(this)); - } - } - - // remove all data dependencies going from/to this node - void removeDDs() - { - removeOutcomingDDs(); - removeIncomingDDs(); - } - - void removeOutcomingUses() - { - while (!useEdges.empty()) - removeUseDependence(*useEdges.begin()); - } - - void removeIncomingUses() - { - while (!userEdges.empty()) { - NodeT *cd = *userEdges.begin(); - // this will remove the reverse control dependence from - // this node - cd->removeUseDependence(static_cast(this)); - } - } - - // remove all direct (top-level) data dependencies going from/to this node - void removeUses() - { - removeOutcomingUses(); - removeIncomingUses(); - } - - // remove all edges from/to this node - void isolate() - { - // remove CD and DD and uses from this node - removeDDs(); - removeUses(); - removeCDs(); - - // if this is head or tail of BB, - // we must take it into account - if (basicBlock) { - // XXX removing the node from BB is in linear time, - // could we do it better? - basicBlock->removeNode(static_cast(this)); - - // if this was the only node in BB, remove the BB - if (basicBlock->empty()) - basicBlock->remove(); - - // if this is a callSite it is no longer part of BBlock, - // so we must remove it from callSites - if (hasSubgraphs()) { -#ifndef NDEBUG - bool ret = -#endif - basicBlock->removeCallSite(static_cast(this)); - assert(ret && "the call site was not in BB's callSites"); - } - - basicBlock = nullptr; - } - } - - // control dependency edges iterators - control_iterator control_begin(void) { return controlDepEdges.begin(); } - const_control_iterator control_begin(void) const { return controlDepEdges.begin(); } - control_iterator control_end(void) { return controlDepEdges.end(); } - const_control_iterator control_end(void) const { return controlDepEdges.end(); } - - // reverse control dependency edges iterators - control_iterator rev_control_begin(void) { return revControlDepEdges.begin(); } - const_control_iterator rev_control_begin(void) const { return revControlDepEdges.begin(); } - control_iterator rev_control_end(void) { return revControlDepEdges.end(); } - const_control_iterator rev_control_end(void) const { return revControlDepEdges.end(); } - - // interference dependency edges iteraotrs - const_interference_iterator interference_begin(void) const { return interferenceDepEdges.begin(); } - interference_iterator interference_begin(void) { return interferenceDepEdges.begin(); } - const_interference_iterator interference_end(void) const { return interferenceDepEdges.end(); } - interference_iterator interference_end(void) { return interferenceDepEdges.end(); } - - // reverse interference dependency edges iterators - const_interference_iterator rev_interference_begin(void) const { return revInterferenceDepEdges.begin(); } - interference_iterator rev_interference_begin(void) { return revInterferenceDepEdges.begin(); } - const_interference_iterator rev_interference_end(void) const { return revInterferenceDepEdges.end(); } - interference_iterator rev_interference_end(void) { return revInterferenceDepEdges.end(); } - - /// NOTE: we have two kinds of data dependencies. - // The first one is when a value is used as an argument - // in another instruction, that is direct (or top-level) dependency. - // The other case is when an instruction reads a value from memory - // which has been written by another instruction. This is - // "indirect" dependency. - // The user can choose whether to use both or just one of - // these dependencies. - - // data dependency edges iterators (indirect dependency) - data_iterator data_begin(void) { return dataDepEdges.begin(); } - const_data_iterator data_begin(void) const { return dataDepEdges.begin(); } - data_iterator data_end(void) { return dataDepEdges.end(); } - const_data_iterator data_end(void) const { return dataDepEdges.end(); } - - // reverse data dependency edges iterators (indirect dependency) - data_iterator rev_data_begin(void) { return revDataDepEdges.begin(); } - const_data_iterator rev_data_begin(void) const { return revDataDepEdges.begin(); } - data_iterator rev_data_end(void) { return revDataDepEdges.end(); } - const_data_iterator rev_data_end(void) const { return revDataDepEdges.end(); } - - // use dependency edges iterators (indirect data dependency - // -- uses of this node e.g. in operands) - use_iterator use_begin() { return useEdges.begin(); } - const_use_iterator use_begin() const { return useEdges.begin(); } - use_iterator use_end() { return useEdges.end(); } - const_use_iterator use_end() const { return useEdges.end(); } - - // user dependency edges iterators (indirect data dependency) - use_iterator user_begin() { return userEdges.begin(); } - const_use_iterator user_begin() const { return userEdges.begin(); } - use_iterator user_end() { return userEdges.end(); } - const_use_iterator user_end() const { return userEdges.end(); } - - size_t getControlDependenciesNum() const { return controlDepEdges.size(); } - size_t getRevControlDependenciesNum() const { return revControlDepEdges.size(); } - size_t getDataDependenciesNum() const { return dataDepEdges.size(); } - size_t getRevDataDependenciesNum() const { return revDataDepEdges.size(); } - size_t getUseDependenciesNum() const { return useEdges.size(); } - size_t getUserDependenciesNum() const { return userEdges.size(); } - - BBlock *getBBlock() { return basicBlock; } - const BBlock *getBBlock() const { return basicBlock; } - - BBlock *setBasicBlock(BBlock *nbb) - { - BBlock *old = basicBlock; - basicBlock = nbb; - return old; - } - - unsigned int getDFSOrder() const - { - return analysisAuxData.dfsorder; - } - - - bool addSubgraph(DependenceGraphT *sub) - { - bool ret = subgraphs.insert(sub).second; - - if (ret) { - // increase references of this graph - // if we added it - sub->ref(); - sub->addCaller(static_cast(this)); - } - - return ret; - } - - DGParameters * - setParameters(DGParameters *params) - { - DGParameters *old = parameters; - - parameters = params; - return old; - } - - const std::set& getSubgraphs(void) const - { - return subgraphs; - } - - bool hasSubgraphs() const - { - return !subgraphs.empty(); - } - - size_t subgraphsNum() const - { - return subgraphs.size(); - } - - DGParameters *getParameters() const - { - return parameters; - } - - KeyT getKey() const - { - return key; - } - - uint32_t getSlice() const { return slice_id; } - uint32_t setSlice(uint32_t sid) - { - uint32_t old = slice_id; - slice_id = sid; - return old; - } - -protected: - - // key uniquely identifying this node in a graph - KeyT key{}; - - // each node has a reference to the DependenceGraph - DependenceGraphT *dg{nullptr}; - -private: - - // add an edge 'ths' --> 'n' to containers of 'ths' and 'n' - static bool _addBidirectionalEdge(NodeT *ths, NodeT *n, - EdgesT& ths_cont, EdgesT& n_cont) { -#ifndef NDEBUG - bool ret1 = -#endif - n_cont.insert(ths); - bool ret2 = ths_cont.insert(n); - - assert(ret1 == ret2 - && "Already had one of the edges, but not the other"); - - return ret2; - } - - // remove edge 'this'-->'n' from control dependencies - static bool _removeBidirectionalEdge(NodeT *ths, NodeT *n, - EdgesT& ths_cont, EdgesT& n_cont) { - bool ret1 = n_cont.erase(ths); -#ifndef NDEBUG - bool ret2 = -#endif - ths_cont.erase(n); - - // must have both or none - assert(ret1 == ret2 && "An edge without rev. or vice versa"); - - return ret1; - } - - ControlEdgesT controlDepEdges; - DataEdgesT dataDepEdges; - UseEdgesT useEdges; - InterferenceEdges interferenceDepEdges; - - // Nodes that have control/dep edge to this node - ControlEdgesT revControlDepEdges; - DataEdgesT revDataDepEdges; - UseEdgesT userEdges; - InterferenceEdges revInterferenceDepEdges; - - // a node can have more subgraphs (i. e. function pointers) - std::set subgraphs; - - // actual parameters if this is a callsite - DGParameters *parameters{nullptr}; - - // id of the slice this nodes is in. If it is 0, it is in no slice - uint32_t slice_id{0}; - - // some analyses need classical CFG edges - // and it is better to have even basic blocks - BBlock *basicBlock{nullptr}; - - // auxiliary data for different analyses - legacy::AnalysesAuxiliaryData analysisAuxData; - friend class legacy::Analysis; -}; - -} // namespace dg - -#endif // _NODE_H_ diff --git a/include/dg/NodesWalk.h b/include/dg/NodesWalk.h deleted file mode 100644 index 8fc9d3c44..000000000 --- a/include/dg/NodesWalk.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef DG_NODES_WALK_H_ -#define DG_NODES_WALK_H_ - -#include -#include - -namespace dg { - -// universal but not very efficient visits tracker -template -struct SetVisitTracker { - std::set _visited{}; - - void visit(Node *n) { _visited.insert(n); } - bool visited(Node *n) const { return _visited.count(n); } -}; - -// universal but not very efficient nodes info -template -struct SuccessorsEdgeChooser { - class range { - Node *_node; - public: - range(Node *n) : _node(n) {} - - auto begin() -> decltype(_node->successors().begin()) { - return _node->successors().begin(); - } - - auto end() -> decltype(_node->successors().end()) { - return _node->successors().end(); - } - - auto begin() const -> decltype(_node->successors().begin()) { - return _node->successors().begin(); - } - - auto end() const -> decltype(_node->successors().end()) { - return _node->successors().end(); - } - }; - - range operator()(Node *n) const { return range(n); } -}; - - - -namespace sfinae { - // std::void_t is from C++17... - template struct make_void { typedef void type;}; - template using void_t = typename make_void::type; -} - -// SFINAE check -template struct has_foreach : std::false_type {}; -template -struct has_foreach> : std::true_type {}; - -template , - typename EdgeChooser = SuccessorsEdgeChooser > -class NodesWalk { - EdgeChooser _chooser{}; - VisitTracker _visits{}; - Queue _queue{}; - - void _enqueue(Node *n) { - _queue.push(n); - _visits.visit(n); - } - - // edge chooser uses operator() - template ::value, Func>::type* = nullptr> - void _run(Func F) { - while (!_queue.empty()) { - Node *current = _queue.pop(); - - F(current); - - for (Node *succ : _chooser(current)) { - if (!_visits.visited(succ)) { - _enqueue(succ); - } - } - } - } - - // edge chooser yields nodes using foreach() - template ::value, Func>::type* = nullptr> - void _run(Func F) { - while (!_queue.empty()) { - Node *current = _queue.pop(); - - F(current); - - _chooser.foreach(current, - [&](Node *n) { - if (!_visits.visited(n)) { - _enqueue(n); - } - }); - } - } - - -public: - NodesWalk() = default; - - NodesWalk(EdgeChooser&& chooser) : _chooser(std::move(chooser)) {} - NodesWalk(VisitTracker&& tracker) : _visits(std::move(tracker)) {} - NodesWalk(VisitTracker&& tracker, EdgeChooser&& chooser) - : _chooser(std::move(chooser)), _visits(std::move(tracker)) {} - - template - void run(Node *start, Func F) { - _enqueue(start); - _run(F); - } - - template - void run(const Container& start, Func F) { - for (Node *n : start) - _enqueue(n); - - _run(F); - } - - template - void run(const std::initializer_list& start, Func F) { - for (Node *n : start) - _enqueue(n); - - _run(F); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/Offset.h b/include/dg/Offset.h deleted file mode 100644 index 6f67f4996..000000000 --- a/include/dg/Offset.h +++ /dev/null @@ -1,165 +0,0 @@ -#ifndef DG_OFFSET_H_ -#define DG_OFFSET_H_ - -#include - -#ifndef NDEBUG -#include -#endif // not NDEBUG - -namespace dg { - -// just a wrapper around uint64_t to -// handle Offset::UNKNOWN somehow easily -// maybe later we'll make it a range -struct Offset -{ - using type = uint64_t; - - // the value used for the unknown offset - static const type UNKNOWN; - - static Offset getUnknown() { - return Offset(Offset::UNKNOWN); - } - - static Offset getZero() { - return Offset(0); - } - - // cast to type - //operator type() { return offset; } - - Offset(type o = UNKNOWN) : offset(o) {} - Offset(const Offset&) = default; - - Offset operator+(const Offset o) const - { - if (offset == UNKNOWN || o.offset == UNKNOWN || - offset >= UNKNOWN - o.offset) { - return UNKNOWN; - } - - return Offset(offset + o.offset); - } - - Offset& operator+=(const Offset o) - { - if (offset == UNKNOWN || o.offset == UNKNOWN || - offset >= UNKNOWN - o.offset) { - offset = UNKNOWN; - } else { - offset += o.offset; - } - - return *this; - } - - Offset& operator=(const Offset o) - { - offset = o.offset; - return *this; - } - - Offset operator-(const Offset& o) const - { - if (offset == UNKNOWN || o.offset == UNKNOWN || - offset < o.offset) { - return Offset(UNKNOWN); - } - - return Offset(offset - o.offset); - } - - Offset& operator-(const Offset& o) - { - if (offset == UNKNOWN || o.offset == UNKNOWN || - offset < o.offset) { - offset = UNKNOWN; - } else { - offset -= o.offset; - } - return *this; - } - - Offset& operator~() - { - if (offset != UNKNOWN) { - offset = ~offset; - } - return *this; - } - - Offset operator~() const - { - if (offset != UNKNOWN) { - return Offset(~offset); - } - return Offset::UNKNOWN; - } - - // strict comparision (no 'maybe' comparions - // that arises due to UNKNOWN) - bool operator<(const Offset& o) const { - return offset < o.offset; - } - - bool operator>(const Offset& o) const { - return offset > o.offset; - } - - bool operator<=(const Offset& o) const { - return offset <= o.offset; - } - - bool operator>=(const Offset& o) const { - return offset >= o.offset; - } - - bool operator==(const Offset& o) const { - return offset == o.offset; - } - - bool operator!=(const Offset& o) const { - return offset != o.offset; - } - - bool inRange(type from, type to) const { - return (offset >= from && offset <= to); - } - - bool isUnknown() const { return offset == UNKNOWN; } - bool isZero() const { return offset == 0; } - - type operator*() const { return offset; } - const type *operator->() const { return &offset; } - -#ifndef NDEBUG - friend std::ostream& operator<<(std::ostream& os, const Offset& o) { - if (o.isUnknown()) - os << "?"; - else - os << o.offset; - return os; - } - - void dump() const { - std::cout << *this << "\n"; - } -#endif // not NDEBUG - - - type offset; -}; - -} // namespace dg - -#include - -namespace std { -template <> struct hash { - size_t operator()(const dg::Offset& o) const { return *o; } -}; -} // namespace std - -#endif diff --git a/include/dg/PointerAnalysis/MemoryObject.h b/include/dg/PointerAnalysis/MemoryObject.h deleted file mode 100644 index b4599c352..000000000 --- a/include/dg/PointerAnalysis/MemoryObject.h +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef DG_MEMORY_OBJECT_H_ -#define DG_MEMORY_OBJECT_H_ - -#include -#include -#include -#include - -#ifndef NDEBUG -#include -#include "dg/PointerAnalysis/PSNode.h" -#endif // not NDEBUG - -#include "PointsToSet.h" - -namespace dg { -namespace pta { - -struct MemoryObject -{ - using PointsToMapT = std::map; - - MemoryObject(/*uint64_t s = 0, bool isheap = false, */PSNode *n = nullptr) - : node(n) /*, is_heap(isheap), size(s)*/ {} - - // where was this memory allocated? for debugging - PSNode *node; - // possible pointers stored in this memory object - PointsToMapT pointsTo; - - PointsToSetT& getPointsTo(const Offset off) { return pointsTo[off]; } - - PointsToMapT::iterator find(const Offset off) { - return pointsTo.find(off); - } - - PointsToMapT::const_iterator find(const Offset off) const { - return pointsTo.find(off); - } - - PointsToMapT::iterator begin() { return pointsTo.begin(); } - PointsToMapT::iterator end() { return pointsTo.end(); } - PointsToMapT::const_iterator begin() const { return pointsTo.begin(); } - PointsToMapT::const_iterator end() const { return pointsTo.end(); } - - bool merge(const MemoryObject& rhs) { - bool changed = false; - for (auto& rit : rhs.pointsTo) { - if (rit.second.empty()) - continue; - changed |= pointsTo[rit.first].add(rit.second); - } - - return changed; - } - - bool addPointsTo(const Offset& off, const Pointer& ptr) - { - assert(ptr.target != nullptr - && "Cannot have NULL target, use unknown instead"); - - return pointsTo[off].add(ptr); - } - - bool addPointsTo(const Offset& off, const PointsToSetT& pointers) - { - if (pointers.empty()) - return false; - return pointsTo[off].add(pointers); - } - - bool addPointsTo(const Offset& off, - std::initializer_list pointers) - { - if (pointers.size() == 0) - return false; - return pointsTo[off].add(pointers); - } - -#ifndef NDEBUG - void dump() const { - std::cout << "MO [" << this << "] for "; - node->dump(); - } - - void dumpv() const { - dump(); - for (const auto& it : pointsTo) { - std::cout << "["; - it.first.dump(); - std::cout << "]"; - for (const auto& ptr : it.second) { - std::cout << " -> "; - ptr.dump(); - std::cout << "\n"; - } - } - std::cout << "\n"; - } - - void print() const { - dump(); - std::cout << "\n"; - } -#endif // not NDEBUG -}; - -} // namespace pta -} // namespace dg - -#endif // DG_MEMORY_OBJECT_H_ diff --git a/include/dg/PointerAnalysis/PSNode.h b/include/dg/PointerAnalysis/PSNode.h deleted file mode 100644 index 148fb5935..000000000 --- a/include/dg/PointerAnalysis/PSNode.h +++ /dev/null @@ -1,765 +0,0 @@ -#ifndef DG_PS_NODE_H_ -#define DG_PS_NODE_H_ - -#include -#include -#include -#include - -#ifndef NDEBUG -#include -#endif // not NDEBUG - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/PointerAnalysis/PointsToSet.h" -#include "dg/SubgraphNode.h" - -namespace dg { -namespace pta { - -enum class PSNodeType { - // these are nodes that just represent memory allocation sites - ALLOC = 1, - LOAD, - STORE, - GEP, - PHI, - CAST, - // support for calls via function pointers. - // The FUNCTION node is the same as ALLOC - // but having it as separate type has the nice - // advantage of type checking - FUNCTION, - // support for interprocedural analysis, - // operands are null terminated. It is a noop, - // just for the user's convenience - CALL, - // call via function pointer - CALL_FUNCPTR, - // return from the subprocedure (in caller), - // synonym to PHI - CALL_RETURN, - // this is the entry node of a subprocedure - // and serves just as no op for our convenience, - // can be optimized away later - ENTRY, - // this is the exit node of a subprocedure - // that returns a value - works as phi node - RETURN, - // nodes which should represent creating - // and joining of threads - FORK, - JOIN, - // node that invalidates allocated memory - // after returning from a function - INVALIDATE_LOCALS, - // node that invalidates memory after calling free - // on a pointer - FREE, - // node that invalidates allocated memory - // after llvm.lifetime.end call - INVALIDATE_OBJECT, - // node that has only one points-to relation - // that never changes - CONSTANT, - // no operation node - this nodes can be used as a branch or join - // node for convenient PointerGraph generation. For example as an - // unified entry to the function or unified return from the function. - // These nodes can be optimized away later. No points-to computation - // is performed on them - NOOP, - // copy whole block of memory - MEMCPY, - // special nodes - NULL_ADDR, - UNKNOWN_MEM, - // tags memory as invalidated - INVALIDATED -}; - -inline const char *PSNodeTypeToCString(enum PSNodeType type) -{ -#define ELEM(t) case t: do {return (#t); }while(0); break; - switch(type) { - ELEM(PSNodeType::ALLOC) - ELEM(PSNodeType::LOAD) - ELEM(PSNodeType::STORE) - ELEM(PSNodeType::GEP) - ELEM(PSNodeType::PHI) - ELEM(PSNodeType::CAST) - ELEM(PSNodeType::FUNCTION) - ELEM(PSNodeType::CALL) - ELEM(PSNodeType::CALL_FUNCPTR) - ELEM(PSNodeType::CALL_RETURN) - ELEM(PSNodeType::FORK) - ELEM(PSNodeType::JOIN) - ELEM(PSNodeType::ENTRY) - ELEM(PSNodeType::RETURN) - ELEM(PSNodeType::CONSTANT) - ELEM(PSNodeType::NOOP) - ELEM(PSNodeType::MEMCPY) - ELEM(PSNodeType::NULL_ADDR) - ELEM(PSNodeType::UNKNOWN_MEM) - ELEM(PSNodeType::FREE) - ELEM(PSNodeType::INVALIDATE_OBJECT) - ELEM(PSNodeType::INVALIDATE_LOCALS) - ELEM(PSNodeType::INVALIDATED) - default: - assert(0 && "unknown PointerGraph type"); - return "Unknown type"; - }; -#undef ELEM -} - -class PointerGraph; -class PointerSubgraph; - -class PSNode : public SubgraphNode { - -public: - using IDType = SubgraphNode::IDType; - -private: - PSNodeType type; - - // in some cases some nodes are kind of paired - like formal and actual - // parameters or call and return node. Here the analasis can store - // such a node - if it needs for generating the PointerGraph - // - it is not used anyhow by the base analysis itself - // XXX: maybe we cold store this somewhere in a map instead of in every - // node (if the map is sparse, it would be much more memory efficient) - PSNode *pairedNode = nullptr; - - // in some cases we need to know from which function the node is - PointerSubgraph *_parent = nullptr; - - unsigned int dfsid = 0; - -public: - /// - // Construct a PSNode - // \param t type of the node - // Different types take different arguments: - // - // ALLOC: no argument - // FUNCTION: no argument - // NOOP: no argument - // ENTRY: no argument - // LOAD: one argument representing pointer to location from where - // we're loading the value (another pointer in this case) - // STORE: first argument is the value (the pointer to be stored) - // in memory pointed by the second argument - // GEP: get pointer to memory on given offset (get element pointer) - // first argument is pointer to the memory, second is the offset - // (as Offset class instance, unknown offset is represented by - // Offset::UNKNOWN constant) - // CAST: cast pointer from one type to other type (like void * to - // int *). The pointers are just copied, so we can optimize - // away this node later. The argument is just the pointer - // (we don't care about types atm.) - // MEMCPY: Copy whole block of memory. - // FUNCTION: Object representing the function in memory - so that it - // can be pointed to and used as an argument to the Pointer - // CONSTANT: node that keeps constant points-to information - // the argument is the pointer it points to - // PHI: phi node that gathers pointers from different paths in CFG - // arguments are null-terminated list of the relevant nodes - // from predecessors - // CALL: represents call of subprocedure, - // XXX: get rid of the arguments here? - // arguments are null-terminated list of nodes that can user - // use arbitrarily - they are not used by the analysis itself. - // The arguments can be used e. g. when mapping call arguments - // back to original CFG. Actually, the CALL node is not needed - // in most cases (just 'inline' the subprocedure into the PointerGraph - // when building it) - // CALL_FUNCPTR: call via function pointer. The argument is the node that - // bears the pointers. - // CALL_RETURN: site where given call returns. Bears the pointers - // returned from the subprocedure. Works like PHI - // RETURN: represents returning value from a subprocedure, - // works as a PHI node - it gathers pointers returned from - // the subprocedure - // INVALIDATE_LOCALS: - // invalidates memory after returning from a function - // FREE: invalidates memory after calling free function on a pointer - - PSNode(IDType id, PSNodeType t) - : SubgraphNode(id), type(t) { - switch(type) { - case PSNodeType::ALLOC: - case PSNodeType::FUNCTION: - // these always points-to itself - // (they points to the node where the memory was allocated) - addPointsTo(this, 0); - break; - default: - break; - } - } - - // Unfortunately, constructors cannot use enums in templates - template - PSNode(IDType id, PSNodeType type, Args&&... args) - : PSNode(id, type) { - addOperand(std::forward(args)...); - } - - virtual ~PSNode() = default; - - PSNodeType getType() const { return type; } - - // an auxiliary method to determine whether a node is a call - bool isCall() const { - return type == PSNodeType::CALL || type == PSNodeType::CALL_FUNCPTR; - } - - void setParent(PointerSubgraph *p) { _parent = p; } - PointerSubgraph *getParent() { return _parent; } - const PointerSubgraph *getParent() const { return _parent; } - - PSNode *getPairedNode() const { return pairedNode; } - void setPairedNode(PSNode *n) { pairedNode = n; } - - bool isNull() const { return type == PSNodeType::NULL_ADDR; } - bool isUnknownMemory() const { return type == PSNodeType::UNKNOWN_MEM; } - bool isInvalidated() const { return type == PSNodeType::INVALIDATED; } - - // make this public, that's basically the only - // reason the PointerGraph node exists, so don't hide it - PointsToSetT pointsTo; - - // convenient helper - bool addPointsTo(PSNode *n, Offset o) { return pointsTo.add(Pointer(n, o)); } - bool addPointsTo(const Pointer& ptr) { return pointsTo.add(ptr); } - bool addPointsTo(const PointsToSetT& ptrs) { return pointsTo.add(ptrs); } - bool addPointsTo(std::initializer_list ptrs) { return pointsTo.add(ptrs); } - - bool doesPointsTo(const Pointer& p) - { - return pointsTo.count(p) == 1; - } - - bool doesPointsTo(PSNode *n, Offset o = 0) - { - return doesPointsTo(Pointer(n, o)); - } - - /// - // Strip all casts from the node as the - // casts do not transform the pointer in any way - PSNode *stripCasts() { - PSNode *node = this; - while (node->getType() == PSNodeType::CAST) - node = node->getOperand(0); - - return node; - } - -#ifndef NDEBUG - void dump() const override { - std::cout << "<"<< getID() << "> " << PSNodeTypeToCString(getType()); - } - - // verbose dump - void dumpv() const override { - dump(); - std::cout << "("; - int n = 0; - for (const auto op : getOperands()) { - if (++n > 1) - std::cout << ", "; - op->dump(); - } - std::cout << ")"; - - for (const auto& ptr : pointsTo) { - std::cout << "\n -> "; - ptr.dump(); - } - std::cout << "\n"; - } -#endif // not NDEBUG - - // FIXME: maybe get rid of these friendships? - friend class PointerAnalysis; - friend class PointerGraph; - - friend void getNodes(std::set& cont, PSNode *n, PSNode* exit, unsigned int dfsnum); -}; - - -// check type of node -template bool isa(const PSNode *n) { - return n->getType() == T; -} - -template -struct PSNodeGetter { - static T *get(PSNode *n) { return static_cast(n); } - static const T *get(const PSNode *n) { return static_cast(n); } - -}; - -template T *_cast(PSNode *n) { - assert(T::get(n) && "Invalid cast"); - return T::get(n); -} - -class PSNodeAlloc : public PSNode { - - // was memory zeroed at initialization or right after allocating? - bool zeroInitialized = false; - // is memory allocated on heap? - bool is_heap = false; - // is it a global value? - bool is_global = false; - // is it a temporary value? (its address cannot be taken) - bool is_temporary = false; - -public: - PSNodeAlloc(IDType id, bool isTemp = false) - : PSNode(id, PSNodeType::ALLOC), is_temporary(isTemp) { - } - - template - static auto get(T *n) -> decltype (PSNodeGetter::get(n)) { - return isa(n) ? - PSNodeGetter::get(n) : nullptr; - } - - static PSNodeAlloc *cast(PSNode *n) { return _cast(n); } - - void setZeroInitialized() { zeroInitialized = true; } - bool isZeroInitialized() const { return zeroInitialized; } - - void setIsHeap() { is_heap = true; } - bool isHeap() const { return is_heap; } - - void setIsGlobal() { is_global = true; } - bool isGlobal() const { return is_global; } - - void setIsTemporary() { is_temporary = true; } - bool isTemporary() const { return is_temporary; } -}; - -#if 0 -class PSNodeTemporaryAlloc : public PSNodeAlloc { - PSNodeTemporaryAlloc(IDType id) - : PSNodeAlloc(id, PSNodeType::ALLOC, /* isTemp */ true) {} - - static PSNodeTemporaryAlloc *get(PSNode *n) { - if (auto alloc = PSNodeAlloc::get(n)) { - return alloc->isTemporary() ? - static_cast(n) : nullptr; - } - - return nullptr; - } -}; -#endif - -class PSNodeConstant : public PSNode { - Offset offset; - -public: - PSNodeConstant(IDType id, PSNode *op, Offset offset) - : PSNode(id, PSNodeType::CONSTANT, op), offset(offset) { - addPointsTo(op, offset); - } - - static PSNodeConstant *get(PSNode *n) { - return isa(n) ? static_cast(n) - : nullptr; - } - - static PSNodeConstant *cast(PSNode *n) { return _cast(n); } - - Pointer getPointer() const { return Pointer(getOperand(0), offset); } - Offset getOffset() const { return offset; } - PSNode *getTarget() { return getOperand(0); } - const PSNode *getTarget() const { return getOperand(0); } -}; - -class PSNodeMemcpy : public PSNode { - Offset len; - -public: - PSNodeMemcpy(IDType id, PSNode *src, PSNode *dest, Offset len) - :PSNode(id, PSNodeType::MEMCPY, src, dest), len(len) {} - - static PSNodeMemcpy *get(PSNode *n) { - return isa(n) ? static_cast(n) : nullptr; - } - - static PSNodeMemcpy *cast(PSNode *n) { return _cast(n); } - - PSNode *getSource() const { return getOperand(0); } - PSNode *getDestination() const { return getOperand(1); } - Offset getLength() const { return len; } -}; - -class PSNodeGep : public PSNode { - Offset offset; - -public: - PSNodeGep(IDType id, PSNode *src, Offset o) - :PSNode(id, PSNodeType::GEP, src), offset(o) {} - - static PSNodeGep *get(PSNode *n) { - return isa(n) ? static_cast(n) : nullptr; - } - - // get() with a check - static PSNodeGep *cast(PSNode *n) { return _cast(n); } - - PSNode *getSource() const { return getOperand(0); } - - void setOffset(uint64_t o) { offset = o; } - Offset getOffset() const { return offset; } -}; - -class PSNodeEntry : public PSNode { - std::string functionName; - std::vector callers; - -public: - PSNodeEntry(IDType id, const std::string& name = "not-known") - :PSNode(id, PSNodeType::ENTRY), functionName(name) {} - - static PSNodeEntry *get(PSNode *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - static PSNodeEntry *cast(PSNode *n) { return _cast(n); } - - void setFunctionName(const std::string& name) { functionName = name; } - const std::string& getFunctionName() const { return functionName; } - - const std::vector& getCallers() const { return callers; } - - bool addCaller(PSNode *n) { - // we suppose there are just few callees, - // so this should be faster than std::set - for (auto p : callers) { - if (p == n) - return false; - } - - callers.push_back(n); - return true; - } -}; - -class PSNodeCall : public PSNode { - // what this call calls? - std::vector callees; - // where it returns? - PSNode *callReturn{nullptr}; - -public: - PSNodeCall(IDType id) - :PSNode(id, PSNodeType::CALL) {} - - PSNodeCall(IDType id, PSNode* op) - :PSNode(id, PSNodeType::CALL_FUNCPTR, op) {} - - static PSNodeCall *get(PSNode *n) { - return (isa(n) || isa(n)) ? - static_cast(n) : nullptr; - } - static PSNodeCall *cast(PSNode *n) { return _cast(n); } - - void setCallReturn(PSNode *callRet) { callReturn = callRet; } - PSNode *getCallReturn() { return callReturn; } - const PSNode *getCallReturn() const { return callReturn; } - - const std::vector& getCallees() const { return callees; } - - bool addCallee(PointerSubgraph *ps) { - // we suppose there are just few callees, - // so this should be faster than std::set - for (PointerSubgraph *p : callees) { - if (p == ps) - return false; - } - - callees.push_back(ps); - return true; - } - -#ifndef NDEBUG - // verbose dump - void dumpv() const override { - PSNode::dumpv(); - if (callReturn) - std::cout << "returns to " << callReturn->getID(); - else - std::cout << "does not return "; - - std::cout << " calls: ["; - int n = 0; - for (const auto op : callees) { - if (++n > 1) - std::cout << ", "; - std::cout << op; - } - std::cout << "]"; - std::cout << "\n"; - } -#endif // not NDEBUG -}; - -class PSNodeCallRet : public PSNode { - // return nodes that go to this call-return node - std::vector returns; - PSNode *call; - -public: - template - PSNodeCallRet(IDType id, Args&&... args) - :PSNode(id, PSNodeType::CALL_RETURN, std::forward(args)...) {} - - static PSNodeCallRet *get(PSNode *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - - static PSNodeCallRet *cast(PSNode *n) { return _cast(n); } - - void setCall(PSNode* c) { call = c; } - PSNode* getCall() { return call; } - const PSNode* getCall() const { return call; } - - const std::vector& getReturns() const { return returns; } - - bool addReturn(PSNode *p) { - // we suppose there are just few callees, - // so this should be faster than std::set - for (auto r : returns) { - if (p == r) - return false; - } - - returns.push_back(p); - return true; - } - -#ifndef NDEBUG - // verbose dump - void dumpv() const override { - PSNode::dumpv(); - std::cout << "Return-site of call " << call->getID() << " rets: ["; - int n = 0; - for (const auto op : returns) { - if (++n > 1) - std::cout << ", "; - op->dump(); - } - std::cout << "]"; - std::cout << "\n"; - } -#endif // not NDEBUG -}; - -class PSNodeRet : public PSNode { - // this node returns control to... - std::vector returns; - -public: - template - PSNodeRet(IDType id, Args&&... args) - :PSNode(id, PSNodeType::RETURN, std::forward(args)...) {} - - static PSNodeRet *get(PSNode *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - - const std::vector& getReturnSites() const { return returns; } - - bool addReturnSite(PSNode *r) { - // we suppose there are just few callees, - // so this should be faster than std::set - for (PSNode *p : returns) { - if (p == r) - return false; - } - - returns.push_back(r); - return true; - } - -#ifndef NDEBUG - // verbose dump - void dumpv() const override { - PSNode::dumpv(); - std::cout << "Returns from: ["; - int n = 0; - for (const auto op : returns) { - if (++n > 1) - std::cout << ", "; - op->dump(); - } - std::cout << "]"; - std::cout << "\n"; - } -#endif // not NDEBUG - -}; - -class PSNodeFork; -class PSNodeJoin; - -class PSNodeFork : public PSNode { - PSNode *callInstruction = nullptr; - std::set joins; - std::set functions_; - -public: - PSNodeFork(IDType id, PSNode* from) - :PSNode(id, PSNodeType::FORK, from) {} - - static PSNodeFork *get(PSNode *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - static PSNodeFork *cast(PSNode *n) { return _cast(n); } - - std::set getJoins() const { return joins; } - - bool addFunction(PSNode * function) { - return functions_.insert(function).second; - } - - std::set functions() const { - return functions_; - } - - void setCallInst(PSNode * callInst) { - callInstruction = callInst; - } - - PSNode * callInst() const { - return callInstruction; - } - - friend class PSNodeJoin; -}; - -class PSNodeJoin : public PSNode { - PSNode *callInstruction = nullptr; - std::set forks_; - std::set functions_; -public: - PSNodeJoin(IDType id) - :PSNode(id, PSNodeType::JOIN) {} - - static PSNodeJoin *get(PSNode *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - static PSNodeJoin *cast(PSNode *n) { return _cast(n); } - - void setCallInst(PSNode *callInst) { - callInstruction = callInst; - } - - PSNode * callInst() const { - return callInstruction; - } - - bool addFunction(PSNode * function) { - return functions_.insert(function).second; - } - - bool addFork(PSNodeFork * fork) { - forks_.insert(fork); - return fork->joins.insert(this).second; - } - - std::set forks() { - return forks_; - } - - std::set functions() const { - return functions_; - } - - friend class PSNodeFork; -}; - -template -struct GetNodeType -{ - using type = PSNode; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeAlloc; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeConstant; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeGep; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeMemcpy; -}; - - -template<> -struct GetNodeType -{ - using type = PSNodeEntry; -}; - -template -struct GetNodeType - ::type> -{ - using type = PSNodeCall; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeFork; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeJoin; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeRet; -}; - -template<> -struct GetNodeType -{ - using type = PSNodeCallRet; -}; - - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/Pointer.h b/include/dg/PointerAnalysis/Pointer.h deleted file mode 100644 index 1cc1e862a..000000000 --- a/include/dg/PointerAnalysis/Pointer.h +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef DG_POINTER_H_ -#define DG_POINTER_H_ - -#include "dg/Offset.h" -#include -#include - -namespace dg { -namespace pta { - -// declare PSNode -class PSNode; - -extern PSNode *NULLPTR; -extern PSNode *UNKNOWN_MEMORY; -extern PSNode *INVALIDATED; - -struct Pointer -{ - Pointer() : target(nullptr) {} - - Pointer(PSNode *n, Offset off) : target(n), offset(off) - { - assert(n && "Cannot have a pointer with nullptr as target"); - } - - // PSNode that allocated the memory this pointer points-to - PSNode *target; - // offset into the memory it points to - Offset offset; - - bool operator<(const Pointer& oth) const - { - return target == oth.target ? offset < oth.offset : target < oth.target; - } - - bool operator==(const Pointer& oth) const - { - return target == oth.target && offset == oth.offset; - } - - bool isNull() const { return target == NULLPTR; } - bool isUnknown() const { return target == UNKNOWN_MEMORY; } - bool isValid() const { return !isNull() && !isUnknown(); } - bool isInvalidated() const { return target == INVALIDATED; } - - size_t hash() const; - -#ifndef NDEBUG - void dump() const; - void print() const; -#endif // not NDEBUG - - -}; - -extern const Pointer UnknownPointer; -extern const Pointer NullPointer; - -} // namespace pta -} // namespace dg - -namespace std{ -template<> struct hash { - size_t operator() (const dg::pta::Pointer& p) const { - return p.hash(); - } -}; -} - -#endif diff --git a/include/dg/PointerAnalysis/PointerAnalysis.h b/include/dg/PointerAnalysis/PointerAnalysis.h deleted file mode 100644 index fa1254b99..000000000 --- a/include/dg/PointerAnalysis/PointerAnalysis.h +++ /dev/null @@ -1,200 +0,0 @@ -#ifndef DG_POINTER_ANALYSIS_H_ -#define DG_POINTER_ANALYSIS_H_ - -#include -#include - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/PointerAnalysis/MemoryObject.h" -#include "dg/PointerAnalysis/PointerGraph.h" -#include "dg/PointerAnalysis/PointerAnalysisOptions.h" -#include "dg/ADT/Queue.h" - -namespace dg { -namespace pta { - -// special nodes and pointers to them -extern PSNode *NULLPTR; -extern PSNode *UNKNOWN_MEMORY; -extern const Pointer NullPointer; -extern const Pointer UnknownPointer; - -class PointerAnalysis -{ - void initPointerAnalysis() { - assert(PG && "Need PointerGraph object"); - } - -protected: - // a set of changed nodes that are going to be - // processed by the analysis - std::vector to_process; - std::vector changed; - - // the pointer state subgraph - PointerGraph *PG{nullptr}; - - const PointerAnalysisOptions options{}; - -public: - - PointerAnalysis(PointerGraph *ps, - const PointerAnalysisOptions& opts) - : PG(ps), options(opts) { - initPointerAnalysis(); - } - - // default options - PointerAnalysis(PointerGraph *ps) : PointerAnalysis(ps, {}) {} - - virtual ~PointerAnalysis() {} - - // takes a PSNode 'where' and 'what' and reference to a vector - // and fills into the vector the objects that are relevant - // for the PSNode 'what' (valid memory states for of this PSNode) - // on location 'where' in PointerGraph - virtual void getMemoryObjects(PSNode *where, const Pointer& pointer, - std::vector& objects) = 0; - - /* - virtual bool addEdge(MemoryObject *from, MemoryObject *to, - Offset off1 = 0, Offset off2 = 0) - { - return false; - } - */ - - /* hooks for analysis - optional. The analysis may do everything - * in getMemoryObjects, but spliting it into before-get-after sequence - * is more readable */ - virtual bool beforeProcessed(PSNode *) { - return false; - } - - virtual bool afterProcessed(PSNode *) { - return false; - } - - PointerGraph *getPG() { return PG; } - const PointerGraph *getPG() const { return PG; } - - - virtual void enqueue(PSNode *n) - { - changed.push_back(n); - } - - virtual void preprocess() { } - - void initialize_queue() { - assert(to_process.empty()); - - PSNode *root = PG->getEntry()->getRoot(); - assert(root && "Do not have root of PG"); - // rely on C++11 move semantics - to_process = PG->getNodes(root); - } - - void queue_globals() { - assert(to_process.empty()); - for (auto *g : PG->getGlobals()) { - to_process.push_back(g); - } - } - - bool iteration() { - assert(changed.empty()); - - for (PSNode *cur : to_process) { - bool enq = false; - enq |= beforeProcessed(cur); - enq |= processNode(cur); - enq |= afterProcessed(cur); - - if (enq) - enqueue(cur); - } - - return !changed.empty(); - } - - void queue_changed() { - unsigned last_processed_num = to_process.size(); - to_process.clear(); - - if (!changed.empty()) { - // DONT std::move - it prevents compiler from copy ellision - to_process = PG->getNodes(changed /* starting set */, - true /* interprocedural */, - last_processed_num /* expected num */); - - // since changed was not empty, - // the to_process must not be empty too - assert(!to_process.empty()); - assert(to_process.size() >= changed.size()); - changed.clear(); - } - } - - bool run(); - - // generic error - // @msg - message for the user - // XXX: maybe create some enum that will represent the error - virtual bool error(PSNode * /*at*/, const char * /*msg*/) - { - // let this on the user - in flow-insensitive analysis this is - // no error, but in flow sensitive it is ... - return false; - } - - // handle specific situation (error) in the analysis - // @return whether the function changed the some points-to set - // (e. g. added pointer to unknown memory) - virtual bool errorEmptyPointsTo(PSNode * /*from*/, PSNode * /*to*/) - { - // let this on the user - in flow-insensitive analysis this is - // no error, but in flow sensitive it is ... - return false; - } - - // adjust the PointerGraph on function pointer call - // @ where is the callsite - // @ what is the function that is being called - virtual bool functionPointerCall(PSNode * /*where*/, PSNode * /*what*/) - { - return false; - } - - // adjust the PointerGraph on when a new function that can be - // spawned by fork is discovered - // @ fork is the callsite - // @ called is the function that is being called - virtual bool handleFork(PSNode * /* fork */, PSNode * /* called */) { - return false; - } - - // handle join of threads - // FIXME: this should be done in the generic pointer analysis, - // we do not need to pass this to the LLVM part... - virtual bool handleJoin(PSNode *) { return false; } - -private: - - // check the sanity of results of pointer analysis - void sanityCheck(); - - bool processNode(PSNode *); - bool processLoad(PSNode *node); - bool processGep(PSNode *node); - bool processMemcpy(PSNode *node); - bool processMemcpy(std::vector& srcObjects, - std::vector& destObjects, - const Pointer& sptr, const Pointer& dptr, - Offset len); -}; - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerAnalysisFI.h b/include/dg/PointerAnalysis/PointerAnalysisFI.h deleted file mode 100644 index 5c7e81ddc..000000000 --- a/include/dg/PointerAnalysis/PointerAnalysisFI.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef DG_ANALYSIS_POINTS_TO_FLOW_INSENSITIVE_H_ -#define DG_ANALYSIS_POINTS_TO_FLOW_INSENSITIVE_H_ - -#include -#include -#include - -#include "PointerAnalysis.h" - -namespace dg { -namespace pta { - -/// -// Flow-insensitive inclusion-based pointer analysis -// -class PointerAnalysisFI : public PointerAnalysis -{ - std::vector> memory_objects; - - void preprocessGEPs() - { - // if a node is in a loop (a scc that has more than one node), - // then every GEP that is also stored to the same memory afterwards - // in the loop will end up with Offset::UNKNOWN after some - // number of iterations (in FI analysis), so we can do that right now - // and save iterations - - assert(getPG() && "Must have PG"); - for (auto& sg : getPG()->getSubgraphs()) { - for (auto& loop : sg->getLoops()) { - for (PSNode *n : loop) { - if (PSNodeGep *gep = PSNodeGep::get(n)) - gep->setOffset(Offset::UNKNOWN); - } - } - } - } - -public: - - PointerAnalysisFI(PointerGraph *ps) : PointerAnalysisFI(ps, {}) {} - - PointerAnalysisFI(PointerGraph *ps, const PointerAnalysisOptions& opts) - : PointerAnalysis(ps, opts) { - memory_objects.reserve(std::max(ps->size() / 100, static_cast(8))); - } - - void preprocess() override { - if (options.preprocessGeps) - preprocessGEPs(); - } - - void getMemoryObjects(PSNode *where, const Pointer& pointer, - std::vector& objects) override - { - // irrelevant in flow-insensitive - (void) where; - PSNode *n = pointer.target; - - // we want to have memory in allocation sites - if (n->getType() == PSNodeType::CAST || n->getType() == PSNodeType::GEP) - n = n->getOperand(0); - else if (n->getType() == PSNodeType::CONSTANT) { - assert(n->pointsTo.size() == 1); - n = (*n->pointsTo.begin()).target; - } - - if (n->getType() == PSNodeType::FUNCTION) - return; - - assert(n->getType() == PSNodeType::ALLOC - || n->getType() == PSNodeType::UNKNOWN_MEM); - - MemoryObject *mo = n->getData(); - if (!mo) { - mo = new MemoryObject(n); - memory_objects.emplace_back(mo); - n->setData(mo); - } - - objects.push_back(mo); - } -}; - -} // namespace pta -} // namespace dg - -#endif // DG_ANALYSIS_POINTS_TO_FLOW_INSENSITIVE_H_ - diff --git a/include/dg/PointerAnalysis/PointerAnalysisFS.h b/include/dg/PointerAnalysis/PointerAnalysisFS.h deleted file mode 100644 index f9c7bcc8a..000000000 --- a/include/dg/PointerAnalysis/PointerAnalysisFS.h +++ /dev/null @@ -1,260 +0,0 @@ -#ifndef DG_ANALYSIS_POINTS_TO_FLOW_SENSITIVE_H_ -#define DG_ANALYSIS_POINTS_TO_FLOW_SENSITIVE_H_ - -#include -#include - -#include "MemoryObject.h" -#include "PointerGraph.h" - -namespace dg { -namespace pta { - -/// -// Flow-sensitive pointer analysis -// -class PointerAnalysisFS : public PointerAnalysis -{ -public: - //using MemoryObjectsSetT = std::set; - using MemoryMapT = std::map>; - - // this is an easy but not very efficient implementation, - // works for testing - PointerAnalysisFS(PointerGraph *ps, - PointerAnalysisOptions opts) - : PointerAnalysis(ps, opts.setPreprocessGeps(false)) - { - assert(opts.preprocessGeps == false - && "Preprocessing GEPs does not work correctly for FS analysis"); - memoryMaps.reserve(ps->size() / 5); - ps->computeLoops(); - } - - PointerAnalysisFS(PointerGraph *ps) : PointerAnalysisFS(ps, {}) {} - - bool beforeProcessed(PSNode *n) override - { - MemoryMapT *mm = n->getData(); - if (mm) - return false; - - // on these nodes the memory map can change - if (needsMerge(n)) { - mm = createMM(); - - // if this is the root of the entry procedure, - // we must propagate the points-to information - // from the globals initialization - if (n == PG->getEntry()->getRoot()) { - mergeGlobalsState(mm, PG->getGlobals()); - } - } else { - // this node can not change the memory map, - // so just add a pointer from the predecessor - // to this map - PSNode *pred = n->getSinglePredecessor(); - mm = pred->getData(); - assert(mm && "No memory map in the predecessor"); - } - - assert(mm && "Did not create the MM"); - - // memory map initialized, set it as data, - // so that we won't initialize it again - n->setData(mm); - - return true; - } - - bool afterProcessed(PSNode *n) override - { - bool changed = false; - PointsToSetT *overwritten = nullptr; - - MemoryMapT *mm = n->getData(); - // we must have the memory map, we created it - // in the beforeProcessed method - assert(mm && "Do not have memory map"); - - // every store that stores to a memory allocated - // not in a loop is a strong update - // FIXME: memcpy can be strong update too - if (n->getType() == PSNodeType::STORE) { - if (!pointsToAllocationInLoop(n->getOperand(1))) - overwritten = &n->getOperand(1)->pointsTo; - } - - // merge information from predecessors if there's - // more of them (if there's just one predecessor - // and this is not a store, the memory map couldn't - // change, so we don't have to do that) - if (needsMerge(n)) { - for (PSNode *p : n->predecessors()) { - if (MemoryMapT *pm = p->getData()) { - // merge pm to mm (but only if pm was already created) - changed |= mergeMaps(mm, pm, overwritten); - } - } - - // interprocedural stuff - merge information from calls - if (auto CR = PSNodeCallRet::get(n)) { - for (auto p : CR->getReturns()) { - if (MemoryMapT *pm = p->getData()) { - // merge pm to mm (but only if pm was already created) - changed |= mergeMaps(mm, pm, overwritten); - } - } - } - if (auto E = PSNodeEntry::get(n)) { - for (auto p : E->getCallers()) { - if (MemoryMapT *pm = p->getData()) { - // merge pm to mm (but only if pm was already created) - changed |= mergeMaps(mm, pm, overwritten); - } - } - } - } - - return changed; - } - - bool functionPointerCall(PSNode *, PSNode *) override { - PG->computeLoops(); - return false; - } - - void getMemoryObjects(PSNode *where, const Pointer& pointer, - std::vector& objects) override - { - MemoryMapT *mm = where->getData(); - assert(mm && "Node does not have memory map"); - - auto I = mm->find(pointer.target); - if (I != mm->end()) { - objects.push_back(I->second.get()); - } - - // if we haven't found any memory object, but this psnode - // is a write to memory, create a new one, so that - // the write has something to write to - if (objects.empty() && canChangeMM(where)) { - MemoryObject *mo = new MemoryObject(pointer.target); - mm->emplace(pointer.target, std::unique_ptr(mo)); - objects.push_back(mo); - } - } - -protected: - - static bool canChangeMM(PSNode *n) { - switch (n->getType()) { - case PSNodeType::STORE: - case PSNodeType::MEMCPY: - case PSNodeType::CALL_FUNCPTR: - // a call via function pointer needs to - // have its own memory map as we dont know - // how the graph will look like after the - // call yet - return true; - case PSNodeType::CALL_RETURN: - // return from function that was called via function - // pointer must have its own memory map from the - // same reason why CALL_FUNCPTR nodes need its - // own memory map - assert(n->getPairedNode()); - return n->getPairedNode()->getType() == PSNodeType::CALL_FUNCPTR; - default: - return false; - } - - return false; - } - - static bool mergeObjects(PSNode *node, - MemoryObject *to, - MemoryObject *from, - PointsToSetT *overwritten) { - bool changed = false; - - for (auto& fromIt : from->pointsTo) { - if (overwritten && - overwritten->count(Pointer(node, fromIt.first))) - continue; - - auto& S = to->pointsTo[fromIt.first]; - for (const auto& ptr : fromIt.second) - changed |= S.add(ptr); - } - - return changed; - } - - // Merge two Memory maps, return true if any new information was created, - // otherwise return false - static bool mergeMaps(MemoryMapT *mm, MemoryMapT *from, - PointsToSetT *overwritten) { - bool changed = false; - for (auto& it : *from) { - PSNode *fromTarget = it.first; - std::unique_ptr& toMo = (*mm)[fromTarget]; - if (toMo == nullptr) - toMo.reset(new MemoryObject(fromTarget)); - - changed |= mergeObjects(fromTarget, toMo.get(), - it.second.get(), overwritten); - } - - return changed; - } - - MemoryMapT *createMM() { - MemoryMapT *mm = new MemoryMapT(); - memoryMaps.emplace_back(mm); - return mm; - } - - bool isOnLoop(const PSNode *n) const { - // if the scc's size > 1, the node is in loop - return n->getParent() ? - (n->getParent()->getLoop(n) != nullptr) : false; - } - - bool pointsToAllocationInLoop(PSNode *n) const { - for (const auto& ptr : n->pointsTo) { - // skip invalidated, null and unknown memory - if (!ptr.isValid() || ptr.isInvalidated()) - continue; - - if (isOnLoop(ptr.target)) - return true; - } - return false; - - } - - static inline bool needsMerge(PSNode *n) { - return n->predecessorsNum() > 1 || - n->predecessorsNum() == 0 || // root node - n->getType() == PSNodeType::CALL_RETURN || // call return is join - canChangeMM(n); - } - - void mergeGlobalsState(MemoryMapT *mm, decltype(PG->getGlobals())& globals) { - for (auto& glob : globals) { - if (MemoryMapT *globmm = glob->getData()) { - mergeMaps(mm, globmm, nullptr); - } - } - } - -private: - - // keep all the maps in order to free the memory - std::vector> memoryMaps; -}; - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerAnalysisFSInv.h b/include/dg/PointerAnalysis/PointerAnalysisFSInv.h deleted file mode 100644 index 69ff24595..000000000 --- a/include/dg/PointerAnalysis/PointerAnalysisFSInv.h +++ /dev/null @@ -1,415 +0,0 @@ -#ifndef DG_ANALYSIS_POINTS_TO_WITH_INVALIDATE_H_ -#define DG_ANALYSIS_POINTS_TO_WITH_INVALIDATE_H_ - -#include -#include "PointerAnalysisFS.h" - -namespace dg { -namespace pta { - -class PointerAnalysisFSInv : public PointerAnalysisFS -{ - static bool canInvalidateMM(PSNode *n) { - return isa(n) || - isa(n) || - isa(n); - } - - static bool needsMerge(PSNode *n) { - return canInvalidateMM(n) || PointerAnalysisFS::needsMerge(n); - } - - static MemoryObject *getOrCreateMO(MemoryMapT *mm, PSNode *target) { - std::unique_ptr& moptr = (*mm)[target]; - if (!moptr) - moptr.reset(new MemoryObject(target)); - - assert(mm->find(target) != mm->end()); - return moptr.get(); - } - -public: - using MemoryMapT = PointerAnalysisFS::MemoryMapT; - - // this is an easy but not very efficient implementation, - // works for testing - PointerAnalysisFSInv(PointerGraph *ps, - PointerAnalysisOptions opts) - : PointerAnalysisFS(ps, opts.setInvalidateNodes(true)) {} - - // default options - PointerAnalysisFSInv(PointerGraph *ps) : PointerAnalysisFSInv(ps, {}) {} - - // NOTE: we must override this method as it is using our "needsMerge" - bool beforeProcessed(PSNode *n) override - { - MemoryMapT *mm = n->getData(); - if (mm) - return false; - - // on these nodes the memory map can change - if (needsMerge(n)) { // root node - mm = createMM(); - - // if this is the root of the entry procedure, - // we must propagate the points-to information - // from the globals initialization - if (n == PG->getEntry()->getRoot()) { - mergeGlobalsState(mm, PG->getGlobals()); - } - } else { - // this node can not change the memory map, - // so just add a pointer from the predecessor - // to this map - PSNode *pred = n->getSinglePredecessor(); - mm = pred->getData(); - assert(mm && "No memory map in the predecessor"); - } - - assert(mm && "Did not create the MM"); - - // memory map initialized, set it as data, - // so that we won't initialize it again - n->setData(mm); - - return true; - } - - bool afterProcessed(PSNode *n) override - { - if (n->getType() == PSNodeType::INVALIDATE_LOCALS) - return handleInvalidateLocals(n); - if (n->getType() == PSNodeType::INVALIDATE_OBJECT) - return invalidateMemory(n); - if (n->getType() == PSNodeType::FREE) - return handleFree(n); - - assert(n->getType() != PSNodeType::FREE && - n->getType() != PSNodeType::INVALIDATE_OBJECT && - n->getType() != PSNodeType::INVALIDATE_LOCALS); - - return PointerAnalysisFS::afterProcessed(n); - } - - static bool isLocal(PSNodeAlloc *alloc, PSNode *where) { - return !alloc->isHeap() && !alloc->isGlobal() && - alloc->getParent() == where->getParent(); - } - - bool containsRemovableLocals(PSNode *where, PointsToSetT& S) { - for (const auto& ptr : S) { - if (ptr.isNull() || ptr.isUnknown() || ptr.isInvalidated()) - continue; - - if (PSNodeAlloc *alloc = PSNodeAlloc::get(ptr.target)) { - if (isLocal(alloc, where) && knownInstance(alloc)) - return true; - } - } - - return false; - } - - // not very efficient - void replaceLocalsWithInv(PSNode *where, PointsToSetT& S1) { - PointsToSetT S; - - for (const auto& ptr : S1) { - if (ptr.isNull() || ptr.isUnknown() || ptr.isInvalidated()) - continue; - - if (PSNodeAlloc *alloc = PSNodeAlloc::get(ptr.target)) { - // if this is not local pointer or it is, - // but we do not know which instance is being destroyed, - // then keep the pointer - if (!isLocal(alloc, where) || !knownInstance(alloc)) - S.add(ptr); - } - } - - S.add(INVALIDATED, 0); - S1.swap(S); - } - - static inline bool isInvalidTarget(const PSNode * const target) { - return target == INVALIDATED || - target == UNKNOWN_MEMORY || - target == NULLPTR; - } - - bool handleInvalidateLocals(PSNode *node) { - bool changed = false; - for (PSNode *pred : node->predecessors()) { - changed |= handleInvalidateLocals(node, pred); - } - return changed; - } - - bool handleInvalidateLocals(PSNode *node, PSNode *pred) - { - MemoryMapT *pmm = pred->getData(); - if (!pmm) { - // predecessor was not processed yet - return false; - } - - MemoryMapT *mm = node->getData(); - assert(mm && "Node does not have a memory map"); - - bool changed = false; - for (auto& I : *pmm) { - if (isInvalidTarget(I.first)) - continue; - - // get or create a memory object for this target - - MemoryObject *mo = getOrCreateMO(mm, I.first); - MemoryObject *pmo = I.second.get(); - - for (auto& it : *mo) { - // remove pointers to locals from the points-to set - if (containsRemovableLocals(node, it.second)) { - replaceLocalsWithInv(node, it.second); - assert(!containsRemovableLocals(node, it.second)); - changed = true; - } - } - - for (auto& it : *pmo) { - PointsToSetT& predS = it.second; - if (predS.empty()) - continue; - - PointsToSetT& S = mo->pointsTo[it.first]; - - // merge pointers from the previous states - // but do not include the pointers - // that _must_ point to destroyed memory - for (const auto& ptr : predS) { - PSNodeAlloc *alloc = PSNodeAlloc::get(ptr.target); - if (alloc && isLocal(alloc, node) && knownInstance(alloc)) { - changed |= S.add(INVALIDATED, 0); - } else - changed |= S.add(ptr); - } - - assert(!S.empty()); - } - } - - return changed; - } - - static void replaceTargetWithInv(PointsToSetT& S1, PSNode *target) { - PointsToSetT S; - for (const auto& ptr : S1) { - if (ptr.target != target) - S.add(ptr); - } - - S.add(INVALIDATED, 0); - S1.swap(S); - } - - bool invalidateMemory(PSNode *node) { - bool changed = false; - for (PSNode *pred : node->predecessors()) { - changed |= invalidateMemory(node, pred); - } - return changed; - } - - bool handleFree(PSNode *node) { - bool changed = false; - for (PSNode *pred : node->predecessors()) { - changed |= invalidateMemory(node, pred, true /* is free */); - } - return changed; - } - - // return true if we know the instance of the object - // (allocations in loop or recursive calls may have - // multiple instances) - bool knownInstance(const PSNode *node) const { - return !isOnLoop(node); - } - - bool invStrongUpdate(const PSNode *operand) const { - // If we are freeing memory through node that - // points to precisely known valid memory that is not allocated - // on a loop, we can do strong update. - // - // TODO: we can do strong update also on must-aliases - // of the invalidated pointer. That is, e.g. for - // free(p), we may do strong update for q if q is must-alias - // of p (no matter the size of p's and q's points-to sets) - if (operand->pointsTo.size() != 1) - return false; - - const auto& ptr = *(operand->pointsTo.begin()); - return !ptr.offset.isUnknown() - && !isInvalidTarget(ptr.target) && knownInstance(ptr.target); - } - - /// - // Check whether we can overwrite the memory object that was used to load - // the pointer into free(). - // Return the PSNode that represents this memory object - PSNode *moFromFreeToOverwrite(PSNode *operand) { - // Bail out if the operand has no pointers yet, - // otherwise we can add invalidated imprecisely - // (the rest of invalidateMemory would not perform strong update) - if (operand->pointsTo.empty()) - return nullptr; - - // invalidate(p) translates to - // 1 = load x - // ... - // invalidate(1) - // Get objects where x may point to. If this object is only one, - // then we know that this object will point to invalid memory - // (no what is its state). - PSNode *strippedOp = operand->stripCasts(); - if (strippedOp->getType() == PSNodeType::LOAD) { - // get the pointer to the memory that holds the pointers - // that are being freed - PSNode *loadOp = strippedOp->getOperand(0); - if (invStrongUpdate(loadOp)) { - return (*(loadOp->pointsTo.begin())).target; - } - } - - return nullptr; - } - - bool overwriteMOFromFree(MemoryMapT *mm, PSNode *target) { - // if we know exactly which memory object - // is being used for freeing the memory, - // we can set it to invalidated - auto mo = getOrCreateMO(mm, target); - if (mo->pointsTo.size() == 1) { - auto& S = mo->pointsTo[0]; - if (S.size() == 1 && (*S.begin()).target == INVALIDATED) { - return false; // no update - } - } - - mo->pointsTo.clear(); - mo->pointsTo[0].add(INVALIDATED, 0); - return true; - } - - bool invalidateMemory(PSNode *node, PSNode *pred, - bool is_free = false) - { - MemoryMapT *pmm = pred->getData(); - if (!pmm) { - // predecessor was not processed yet - return false; - } - - MemoryMapT *mm = node->getData(); - assert(mm && "Node does not have memory map"); - - bool changed = false; - - PSNode *operand = node->getOperand(0); - PSNode *strong_update = nullptr; - // if we call e.g. free(load p), then the contents of - // the memory pointed by p will point - // to invalidated memory (we can do this when we - // know precisely what is the memory). - if (is_free) { - strong_update = moFromFreeToOverwrite(operand); - if (strong_update) - changed |= overwriteMOFromFree(mm, strong_update); - } - - for (auto& I : *pmm) { - assert(I.first && "nullptr as target"); - - if (isInvalidTarget(I.first)) - continue; - - // strong update on this variable? - if (strong_update == I.first) - continue; - - // get or create a memory object for this target - MemoryObject *mo = getOrCreateMO(mm, I.first); - MemoryObject *pmo = I.second.get(); - - // Remove references to invalidated memory from mo - // if the invalidated object is just one. - // Otherwise, add the invalidated pointer to the points-to sets - // (strong vs. weak update) as we do not know which - // object is actually being invalidated. - for (auto& it : *mo) { - if (invStrongUpdate(operand)) { // strong update - const auto& ptr = *(operand->pointsTo.begin()); - if (ptr.isUnknown()) - changed |= it.second.add(INVALIDATED, 0); - else if (ptr.isNull() || ptr.isInvalidated()) - continue; - else if (it.second.pointsToTarget(ptr.target)) { - replaceTargetWithInv(it.second, ptr.target); - assert(!it.second.pointsToTarget(ptr.target)); - changed = true; - } - } else { // weak update - for (const auto& ptr : operand->pointsTo) { - if (ptr.isNull() || ptr.isInvalidated()) - continue; - - // invalidate on unknown memory yields invalidate for - // each element - if (ptr.isUnknown() || it.second.pointsToTarget(ptr.target)) { - changed |= it.second.add(INVALIDATED, 0); - } - } - } - } - - // merge pointers from pmo to mo, but skip - // the pointers that may point to the freed memory - for (auto& it : *pmo) { - PointsToSetT& predS = it.second; - if (predS.empty()) // keep the map clean - continue; - - PointsToSetT& S = mo->pointsTo[it.first]; - - // merge pointers from the previous states - // but do not include the pointers - // that may point to freed memory. - // These must be replaced with invalidated. - for (const auto& ptr : predS) { - if (ptr.isValid() && // if the ptr is null or unkown, - // we want to copy it - operand->pointsTo.pointsToTarget(ptr.target)) { - if (!invStrongUpdate(operand)) { - // we still want to copy the original pointer - // if we cannot perform strong update - // on this invalidated memory - changed |= S.add(ptr); - } - changed |= S.add(INVALIDATED, 0); - } else { - // this is a pointer to some memory that was not - // invalidated, so merge it into the points-to set - changed |= S.add(ptr); - } - } - - assert(!S.empty()); - } - } - - return changed; - } -}; - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerAnalysisOptions.h b/include/dg/PointerAnalysis/PointerAnalysisOptions.h deleted file mode 100644 index 5443aac32..000000000 --- a/include/dg/PointerAnalysis/PointerAnalysisOptions.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef DG_POINTER_ANALYSIS_OPTIONS_H_ -#define DG_POINTER_ANALYSIS_OPTIONS_H_ - -#include "dg/AnalysisOptions.h" - -namespace dg { - -struct PointerAnalysisOptions : AnalysisOptions { - // Preprocess GEP nodes such that the offset - // is directly set to UNKNOWN if we can identify - // that it will be the result of the computation - // (saves iterations) - bool preprocessGeps{true}; - - // Should the analysis keep track of invalidate - // (e.g. freed) memory? Pointers pointing to such - // memory are then represented as pointing to - // INVALIDATED object. - bool invalidateNodes{false}; - - PointerAnalysisOptions& setInvalidateNodes(bool b) { invalidateNodes = b; return *this;} - PointerAnalysisOptions& setPreprocessGeps(bool b) { preprocessGeps = b; return *this;} - - // Perform maximally this number of iterations. - // If exceeded, the analysis is terminated and points-to sets - // of the unprocessed nodes are set to {}. - size_t maxIterations{0}; -}; - -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerGraph.h b/include/dg/PointerAnalysis/PointerGraph.h deleted file mode 100644 index 2803009fd..000000000 --- a/include/dg/PointerAnalysis/PointerGraph.h +++ /dev/null @@ -1,353 +0,0 @@ -#ifndef DG_POINTER_GRAPH_H_ -#define DG_POINTER_GRAPH_H_ - -#include "dg/ADT/Queue.h" -#include "dg/SubgraphNode.h" -#include "dg/CallGraph/CallGraph.h" -#include "dg/PointerAnalysis/PSNode.h" -#include "dg/BFS.h" -#include "dg/SCC.h" -#include "dg/util/debug.h" - -#include -#include -#include -#include -#include -#include - -namespace dg { -namespace pta { - -// special nodes and pointers to them -extern PSNode *NULLPTR; -extern PSNode *UNKNOWN_MEMORY; -extern const Pointer NullPointer; -extern const Pointer UnknownPointer; - -class PointerGraph; - -// A single procedure in Pointer Graph -class PointerSubgraph { - friend class PointerGraph; - - unsigned _id{0}; - - PointerSubgraph(unsigned id, PSNode *r1, PSNode *va = nullptr) - : _id(id), root(r1), vararg(va) {} - - PointerSubgraph() = default; - PointerSubgraph(const PointerSubgraph&) = delete; - - // non-trivial strongly connected components - bool _computed_loops{false}; - std::vector> _loops; - std::unordered_map _node_to_loop; - -public: - PointerSubgraph(PointerSubgraph&&) = default; - - unsigned getID() const { return _id; } - - // FIXME: make the attrs private - - // the first node of the subgraph. XXX: rename to entry - PSNode *root{nullptr}; - - // return nodes of this graph - std::set returnNodes{}; - - // this is the node where we gather the variadic-length arguments - PSNode *vararg{nullptr}; - - PSNode *getRoot() { return root; } - const PSNode *getRoot() const { return root; } - - bool computedLoops() const { return _computed_loops; } - - // information about loops in this subgraph - const std::vector *getLoop(const PSNode *nd) const { - assert(_computed_loops && "Call computeLoops() first"); - - auto it = _node_to_loop.find(nd); - assert(it == _node_to_loop.end() || it->second < _loops.size()); - return it == _node_to_loop.end() ? nullptr : &_loops[it->second]; - } - - const std::vector>& getLoops() const { - assert(_computed_loops && "Call computeLoops() first"); - return _loops; - } - - const std::vector>& getLoops() { - if (!computedLoops()) - computeLoops(); - return _loops; - } - - // FIXME: remember just that a node is on loop, not the whole loops - void computeLoops(); -}; - -// IDs of special nodes -enum PointerGraphReservedIDs { - ID_UNKNOWN = 1, - ID_NULL = 2, - ID_INVALIDATED = 3, - LAST_RESERVED_ID = 3 -}; - -/// -// Basic graph for pointer analysis -// -- contains CFG graphs for all procedures of the program. -class PointerGraph -{ - unsigned int dfsnum{0}; - - // root of the pointer state subgraph - PointerSubgraph *_entry{nullptr}; - - using NodesT = std::vector>; - using GlobalNodesT = std::vector; - using SubgraphsT = std::vector>; - - NodesT nodes; - SubgraphsT _subgraphs; - - // Take care of assigning ids to new nodes - unsigned int last_node_id = PointerGraphReservedIDs::LAST_RESERVED_ID; - unsigned int getNewNodeId() { return ++last_node_id; } - - GenericCallGraph callGraph; - GlobalNodesT _globals; - - // check for correct count of variadic arguments - template - constexpr static ssize_t expected_args_size() { - // C++14 TODO: replace this atrocity with a switch - return type == PSNodeType::NOOP || - type == PSNodeType::ENTRY || - type == PSNodeType::FUNCTION || - type == PSNodeType::FORK || - type == PSNodeType::JOIN ? 0 : - type == PSNodeType::CAST || - type == PSNodeType::LOAD || - type == PSNodeType::INVALIDATE_OBJECT || - type == PSNodeType::INVALIDATE_LOCALS || - type == PSNodeType::FREE ? 1 : - type == PSNodeType::STORE || - type == PSNodeType::CONSTANT ? 2 : - type == PSNodeType::CALL || - type == PSNodeType::CALL_FUNCPTR || - type == PSNodeType::CALL_RETURN || - type == PSNodeType::PHI || - type == PSNodeType::RETURN ? actual_size : -1; - } - - // C++17 TODO: - // * replace the two definitions of nodeFactory with one using - // `if constexpr (needs_type)` - // * replace GetNodeType with a chain of `if constexpr` expressions - template::type> - typename std::enable_if::value, Node*>::type - nodeFactory(Args&&... args) { - return new Node(getNewNodeId(), std::forward(args)...); - } - - // we need to check that the number of arguments is correct with general - // PSNode - template::type> - typename std::enable_if::value, Node*>::type - nodeFactory(Args&&... args) { - static_assert( - expected_args_size() == sizeof...(args), - "Incorrect number of arguments"); - return new Node(getNewNodeId(), Type, std::forward(args)...); - } - -public: - PointerGraph() { - // nodes[0] represents invalid node (the node with id 0) - nodes.emplace_back(nullptr); - // the first several nodes are special nodes. For now, we just replace - // them with nullptr, as those are created statically <-- FIXME! - nodes.emplace_back(nullptr); - nodes.emplace_back(nullptr); - nodes.emplace_back(nullptr); - assert(nodes.size() - 1 == PointerGraphReservedIDs::LAST_RESERVED_ID); - initStaticNodes(); - } - - void initStaticNodes(); - - PointerSubgraph *createSubgraph(PSNode *root, - PSNode *vararg = nullptr) { - // NOTE: id of the subgraph is always index in _subgraphs + 1 - _subgraphs.emplace_back( - new PointerSubgraph(static_cast(_subgraphs.size()) + 1, - root, vararg)); - return _subgraphs.back().get(); - } - - template - PSNode *create(Args&&... args) { - PSNode *n = nodeFactory(std::forward(args)...); - nodes.emplace_back(n); // C++17 returns a referece - assert(n->getID() == nodes.size() - 1); - return n; - } - - // create a global node. Global nodes will be processed - // in the same order in which they are created. - // The global nodes are processed only once before the - // analysis starts. - template - PSNode *createGlobal(Args&&... args) { - PSNode *n = create(std::forward(args)...); - _globals.push_back(n); // C++17 returns a referece - assert(n->getID() == nodes.size() - 1); - return n; - } - - bool registerCall(PSNode *a, PSNode *b) { - return callGraph.addCall(a, b); - } - - GenericCallGraph& getCallGraph() { return callGraph; } - const GenericCallGraph& getCallGraph() const { return callGraph; } - const SubgraphsT& getSubgraphs() const { return _subgraphs; } - - const NodesT& getNodes() const { return nodes; } - const GlobalNodesT& getGlobals() const { return _globals; } - size_t size() const { return nodes.size() + _globals.size(); } - - void computeLoops(); - - PointerGraph(PointerGraph&&) = default; - PointerGraph& operator=(PointerGraph&&) = default; - PointerGraph(const PointerGraph&) = delete; - PointerGraph operator=(const PointerGraph&) = delete; - - PointerSubgraph *getEntry() const { return _entry; } - void setEntry(PointerSubgraph *e); - - void remove(PSNode *nd); - - // get nodes in BFS order and store them into - // the container - template - std::vector getNodes(const ContainerOrNode& start, - bool interprocedural = true, - unsigned expected_num = 0) - { - ++dfsnum; - - std::vector cont; - if (expected_num != 0) - cont.reserve(expected_num); - - struct DfsIdTracker { - const unsigned dfsnum; - DfsIdTracker(unsigned dnum) : dfsnum(dnum) {} - - void visit(PSNode *n) { n->dfsid = dfsnum; } - bool visited(PSNode *n) const { return n->dfsid == dfsnum; } - }; - - // iterate over successors and call (return) edges - struct EdgeChooser { - const bool interproc; - EdgeChooser(bool inter = true) : interproc(inter) {} - - void foreach(PSNode *cur, std::function Dispatch) { - if (interproc) { - if (PSNodeCall *C = PSNodeCall::get(cur)) { - for (auto subg : C->getCallees()) { - Dispatch(subg->root); - } - // we do not need to iterate over succesors - // if we dive into the procedure (as we will - // return via call return) - // NOTE: we must iterate over successors if the - // function is undefined - if (!C->getCallees().empty()) - return; - } else if (PSNodeRet *R = PSNodeRet::get(cur)) { - for (auto ret : R->getReturnSites()) { - Dispatch(ret); - } - if (!R->getReturnSites().empty()) - return; - } - } - - for (auto s : cur->successors()) - Dispatch(s); - } - }; - - DfsIdTracker visitTracker(dfsnum); - EdgeChooser chooser(interprocedural); - BFS bfs(visitTracker, chooser); - - bfs.run(start, [&cont](PSNode *n) { cont.push_back(n); }); - - return cont; - } - -}; - -/// -// get nodes reachable from n (including n), -// stop at node 'exit' (excluding) if not set to null -inline std::set -getReachableNodes(PSNode *n, - PSNode *exit = nullptr, - bool interproc = true) -{ - ADT::QueueFIFO fifo; - std::set cont; - - assert(n && "No starting node given."); - fifo.push(n); - - while (!fifo.empty()) { - PSNode *cur = fifo.pop(); - if (!cont.insert(cur).second) - continue; // we already visited this node - - for (PSNode *succ : cur->successors()) { - assert(succ != nullptr); - - if (succ == exit) - continue; - - fifo.push(succ); - } - - if (interproc) { - if (PSNodeCall *C = PSNodeCall::get(cur)) { - for (auto subg : C->getCallees()) { - if (subg->root == exit) - continue; - fifo.push(subg->root); - } - } else if (PSNodeRet *R = PSNodeRet::get(cur)) { - for (auto ret : R->getReturnSites()) { - if (ret == exit) - continue; - fifo.push(ret); - } - } - } - } - - return cont; -} - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerGraphOptimizations.h b/include/dg/PointerAnalysis/PointerGraphOptimizations.h deleted file mode 100644 index e9aea37f2..000000000 --- a/include/dg/PointerAnalysis/PointerGraphOptimizations.h +++ /dev/null @@ -1,131 +0,0 @@ -#ifndef DG_POINTER_SUBGRAPH_OPTIMIZATIONS_H_ -#define DG_POINTER_SUBGRAPH_OPTIMIZATIONS_H_ - -#include "PointsToMapping.h" - -namespace dg { -namespace pta { - -class PSNoopRemover { - PointerGraph *G; -public: - PSNoopRemover(PointerGraph *g) : G(g) {} - unsigned run(); -}; - -// try to remove loads/stores that are provably -// loads and stores of unknown memory -// (these usually correspond to integers) -class PSUnknownsReducer { - using MappingT = PointsToMapping; - - PointerGraph *G; - MappingT mapping; - - unsigned removed = 0; - - void processAllocs(); - -public: - PSUnknownsReducer(PointerGraph *g) : G(g) {} - - MappingT& getMapping() { return mapping; } - const MappingT& getMapping() const { return mapping; } - - unsigned run() { - processAllocs(); - return removed; - }; -}; - -class PSEquivalentNodesMerger { -public: - using MappingT = PointsToMapping; - - PSEquivalentNodesMerger(PointerGraph *g) - : G(g), merged_nodes_num(0) { - mapping.reserve(32); - } - - MappingT& getMapping() { return mapping; } - const MappingT& getMapping() const { return mapping; } - - unsigned getNumOfMergedNodes() const { - return merged_nodes_num; - } - - unsigned run() { - mergeCasts(); - return merged_nodes_num; - } - -private: - // get rid of all casts - void mergeCasts(); - - // merge node1 and node2 (node2 will be - // the representant and node1 will be removed, - // mapping will be set to node1 -> node2) - void merge(PSNode *node1, PSNode *node2); - - PointerGraph *G; - // map nodes to its equivalent representant - MappingT mapping; - - unsigned merged_nodes_num; -}; - -class PointerGraphOptimizer { - using MappingT = PointsToMapping; - - PointerGraph *G; - MappingT mapping; - - unsigned removed = 0; -public: - PointerGraphOptimizer(PointerGraph *g) : G(g) {} - - void removeNoops() { - PSNoopRemover remover(G); - removed += remover.run(); - } - - void removeUnknowns() { - PSUnknownsReducer reducer(G); - if (auto r = reducer.run()) { - mapping.merge(std::move(reducer.getMapping())); - removed += r; - } - } - - void removeEquivalentNodes() { - PSEquivalentNodesMerger merger(G); - if (auto r = merger.run()) { - mapping.merge(std::move(merger.getMapping())); - removed += r; - } - } - - unsigned run() { - removeNoops(); - removeEquivalentNodes(); - removeUnknowns(); - // need to call this once more because - // the optimizations may have created - // the same operands in a phi nodes, - // which breaks the validity of the graph - removeEquivalentNodes(); - - return removed; - } - - unsigned getNumOfRemovedNodes() const { return removed; } - MappingT& getMapping() { return mapping; } - const MappingT& getMapping() const { return mapping; } -}; - - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointerGraphValidator.h b/include/dg/PointerAnalysis/PointerGraphValidator.h deleted file mode 100644 index efc1c6203..000000000 --- a/include/dg/PointerAnalysis/PointerGraphValidator.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef DG_POINTER_SUBGRAPH_VALIDATOR_H_ -#define DG_POINTER_SUBGRAPH_VALIDATOR_H_ - -#include -#include "dg/PointerAnalysis/PointerGraph.h" - -namespace dg { -namespace pta { - - -/** - * Take PointerGraph instance and check whether it is not broken - * FIXME: make this private to PointerGraph - */ -class PointerGraphValidator { - /* These methods return true if the graph is invalid */ - bool checkEdges(); - bool checkNodes(); - bool checkOperands(); - - // do not check for the connectivity of the graph - bool no_connectivity; - -protected: - const PointerGraph *PS; - - std::string errors{}; - std::string warnings{}; - - virtual bool reportInvalOperands(const PSNode *n, const std::string& user_err = ""); - virtual bool reportInvalEdges(const PSNode *n, const std::string& user_err = ""); - virtual bool reportInvalNode(const PSNode *n, const std::string& user_err = ""); - virtual bool reportUnreachableNode(const PSNode *); - - virtual bool warn(const PSNode *n, const std::string& warning); - -public: - PointerGraphValidator(const PointerGraph *ps, bool no_conn = false) - : no_connectivity(no_conn), PS(ps) {} - virtual ~PointerGraphValidator() = default; - - bool validate(); - - const std::string& getErrors() const { return errors; } - const std::string& getWarnings() const { return warnings; } -}; - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointsToMapping.h b/include/dg/PointerAnalysis/PointsToMapping.h deleted file mode 100644 index 3e8725d79..000000000 --- a/include/dg/PointerAnalysis/PointsToMapping.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef DG_POINTS_TO_MAPPING_H_ -#define DG_POINTS_TO_MAPPING_H_ - -#include -#include "PSNode.h" - -namespace dg { -namespace pta { - -// this is a wrapper around a map that -// is supposed to keep mapping of program values -// to pointer analysis nodes that are actually not -// created (or that are removed later by an analysis). -template -class PointsToMapping { - using MappingT = std::unordered_map; - using iterator = typename MappingT::iterator; - using const_iterator = typename MappingT::const_iterator; - - MappingT mapping; -public: - void reserve(size_t s) { - mapping.reserve(s); - } - - size_t size() const { return mapping.size(); } - - PSNode *get(ValT val) const { - auto it = mapping.find(val); - if (it == mapping.end()) - return nullptr; - - return it->second; - } - - void add(ValT val, PSNode *nd) { - auto it = mapping.find(val); - assert(it == mapping.end()); - mapping.emplace_hint(it, val, nd); - } - - void set(ValT val, PSNode *nd) { - mapping[val] = nd; - } - - // merge some other points-to mapping to this one - // (destroying the other one). If there are - // duplicates values, than the ones from 'rhs' - // are used - void merge(PointsToMapping&& rhs) { - // merge values from this map to rhs (making preference for - // duplicate rhs values). - rhs.mapping.insert(mapping.begin(), mapping.end()); - rhs.mapping.swap(mapping); - rhs.mapping.clear(); - } - - // compose this mapping with some other mapping: - // (PSNode * -> PSNode *) o (ValT -> PSNode *) - // leads to (ValT -> PSNode *). - void compose(PointsToMapping&& rhs) { - for (auto& it : mapping) { - if (PSNode *rhs_node = rhs.get(it.second)) { - it.second = rhs_node; - } - } - } - - iterator begin() { return mapping.begin(); } - iterator end() { return mapping.end(); } - const_iterator begin() const { return mapping.begin(); } - const_iterator end() const { return mapping.end(); } -}; - - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointsToSet.h b/include/dg/PointerAnalysis/PointsToSet.h deleted file mode 100644 index 1c68246c6..000000000 --- a/include/dg/PointerAnalysis/PointsToSet.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef DG_POINTS_TO_SET_H_ -#define DG_POINTS_TO_SET_H_ - -#include "dg/PointerAnalysis/PointsToSets/OffsetsSetPointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/SimplePointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/SeparateOffsetsPointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/PointerIdPointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/SmallOffsetsPointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/AlignedSmallOffsetsPointsToSet.h" -#include "dg/PointerAnalysis/PointsToSets/AlignedPointerIdPointsToSet.h" - -namespace dg { -namespace pta { - -using PointsToSetT = PointerIdPointsToSet; -using PointsToMapT = std::map; - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointsToSets/AlignedPointerIdPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/AlignedPointerIdPointsToSet.h deleted file mode 100644 index 250140e89..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/AlignedPointerIdPointsToSet.h +++ /dev/null @@ -1,258 +0,0 @@ -#ifndef ALIGNEDBITVECTORPOINTSTOSET_H -#define ALIGNEDBITVECTORPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include -#include -#include - -namespace dg { -namespace pta { - -class PSNode; - -class AlignedPointerIdPointsToSet { - - static const unsigned int multiplier = 4; - - ADT::SparseBitvector pointers; - std::set overflowSet; - static std::map ids; //pointers are numbered 1, 2, ... - static std::vector idVector; //starts from 0 (pointer = idVector[id - 1]) - - //if the pointer doesn't have ID, it's assigned one - size_t getPointerID(const Pointer& ptr) const { - auto it = ids.find(ptr); - if(it != ids.end()) { - return it->second; - } - idVector.push_back(ptr); - return ids.emplace_hint(it, ptr, ids.size() + 1)->second; - } - - bool addWithUnknownOffset(PSNode* node) { - removeAny(node); - return !pointers.set(getPointerID({node, Offset::UNKNOWN})); - } - - bool isOffsetValid(Offset off) const { - return off.isUnknown() || *off % multiplier == 0; - } - -public: - AlignedPointerIdPointsToSet() = default; - AlignedPointerIdPointsToSet(std::initializer_list elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - return add(Pointer(target,off)); - } - - bool add(const Pointer& ptr) { - if(has({ptr.target, Offset::UNKNOWN})) { - return false; - } - if(ptr.offset.isUnknown()) { - return addWithUnknownOffset(ptr.target); - } - if(isOffsetValid(ptr.offset)) { - return !pointers.set(getPointerID(ptr)); - } - return overflowSet.insert(ptr).second; - } - - bool add(const AlignedPointerIdPointsToSet& S) { - bool changed = pointers.set(S.pointers); - for (const auto& ptr : S.overflowSet) { - changed |= overflowSet.insert(ptr).second; - } - return changed; - } - - bool remove(const Pointer& ptr) { - if(isOffsetValid(ptr.offset)) { - return pointers.unset(getPointerID(ptr)); - } - return overflowSet.erase(ptr) != 0; - } - - bool remove(PSNode *target, Offset offset) { - return remove(Pointer(target,offset)); - } - - bool removeAny(PSNode *target) { - std::vector toRemove; - for (const auto& ptrID : pointers) { - if(idVector[ptrID - 1].target == target) { - toRemove.push_back(ptrID); - } - } - - for (auto ptrID : toRemove) { - pointers.unset(ptrID); - } - - bool changed = false; - auto it = overflowSet.begin(); - while(it != overflowSet.end()) { - if(it->target == target) { - it = overflowSet.erase(it); - // Note: the iterator to the next element is now in it - changed = true; - } else { - it++; - } - } - return changed || !toRemove.empty(); - } - - void clear() { - pointers.reset(); - overflowSet.clear(); - } - - bool pointsTo(const Pointer& ptr) const { - if(isOffsetValid(ptr.offset)) { - return pointers.get(getPointerID(ptr)); - } - return overflowSet.find(ptr) != overflowSet.end(); - } - - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) - || pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - for(const auto& kv : ids) { - if(kv.first.target == target && pointers.get(kv.second)) { - return true; - } - } - for (const auto& ptr : overflowSet) { - if (ptr.target == target) - return true; - } - return false; - } - - bool isSingleton() const { - return (pointers.size() == 1 && overflowSet.empty()) - || (pointers.empty() && overflowSet.size() == 1); - } - - bool empty() const { - return pointers.empty() && overflowSet.empty(); - } - - size_t count(const Pointer& ptr) const { - return pointsTo(ptr); - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { - return pointsToTarget(UNKNOWN_MEMORY); - } - - bool hasNull() const { - return pointsToTarget(NULLPTR); - - } - - bool hasInvalidated() const { - return pointsToTarget(INVALIDATED); - } - - size_t size() const { - return pointers.size() + overflowSet.size(); - } - - void swap(AlignedPointerIdPointsToSet& rhs) { - pointers.swap(rhs.pointers); - overflowSet.swap(rhs.overflowSet); - } - - size_t overflowSetSize() const { - return overflowSet.size(); - } - - static unsigned int getMultiplier() { - return multiplier; - } - - class const_iterator { - - typename ADT::SparseBitvector::const_iterator bitvector_it; - typename ADT::SparseBitvector::const_iterator bitvector_end; - typename std::set::const_iterator set_it; - bool secondContainer; - - const_iterator(const ADT::SparseBitvector& pointers, const std::set& overflow, bool end = false) : - bitvector_it(end ? pointers.end() : pointers.begin()), - bitvector_end(pointers.end()), - set_it(end ? overflow.end() : overflow.begin()), - secondContainer(end) { - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } - - public: - const_iterator& operator++() { - if(!secondContainer) { - bitvector_it++; - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } else { - set_it++; - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - if(!secondContainer) { - return Pointer(idVector[*bitvector_it - 1]); - } - return *set_it; - } - - bool operator==(const const_iterator& rhs) const { - return bitvector_it == rhs.bitvector_it - && set_it == rhs.set_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class AlignedPointerIdPointsToSet; - }; - - const_iterator begin() const { return const_iterator(pointers, overflowSet); } - const_iterator end() const { return const_iterator(pointers, overflowSet, true /* end */); } - - friend class const_iterator; -}; - -} // namespace pta -} // namespace dg - -#endif /* ALIGNEDBITVECTORPOINTSTOSET_H */ - diff --git a/include/dg/PointerAnalysis/PointsToSets/AlignedSmallOffsetsPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/AlignedSmallOffsetsPointsToSet.h deleted file mode 100644 index 7dd672242..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/AlignedSmallOffsetsPointsToSet.h +++ /dev/null @@ -1,265 +0,0 @@ -#ifndef ALIGNEDOFFSETSPOINTSTOSET_H -#define ALIGNEDOFFSETSPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include -#include -#include - -namespace dg { -namespace pta { - -class PSNode; - -class AlignedSmallOffsetsPointsToSet { - - static const unsigned int multiplier = 4; //offsets that are divisible by this value are stored in bitvector up to 62 * multiplier - - ADT::SparseBitvector pointers; - std::set oddPointers; - static std::map ids; //nodes are numbered 1,2, ... - static std::vector idVector; //starts from 0 (node = idVector[id - 1]) - - //if the node doesn't have ID, it's assigned one - size_t getNodeID(PSNode *node) const { - auto it = ids.find(node); - if(it != ids.end()) { - return it->second; - } - idVector.push_back(node); - return ids.emplace_hint(it, node, ids.size() + 1)->second; - } - - size_t getNodePosition(PSNode *node) const { - return ((getNodeID(node) - 1) * 64); - } - - size_t getPosition(PSNode *node, Offset off) const { - if(off.isUnknown()) { - return getNodePosition(node) + 63; - } - return getNodePosition(node) + (*off / multiplier); - } - - bool isOffsetValid(Offset off) const { - return off.isUnknown() - || (*off <= 62 * multiplier && *off % multiplier == 0); - } - - bool addWithUnknownOffset(PSNode *target) { - removeAny(target); - return !pointers.set(getPosition(target, Offset::UNKNOWN)); - } - -public: - AlignedSmallOffsetsPointsToSet() = default; - AlignedSmallOffsetsPointsToSet(std::initializer_list elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - if(has({target, Offset::UNKNOWN})) { - return false; - } - if(off.isUnknown()) { - return addWithUnknownOffset(target); - } - if(isOffsetValid(off)) { - return !pointers.set(getPosition(target, off)); - } - return oddPointers.emplace(target,off).second; - } - - bool add(const Pointer& ptr) { - return add(ptr.target, ptr.offset); - } - - bool add(const AlignedSmallOffsetsPointsToSet& S) { - bool changed = pointers.set(S.pointers); - for (const auto& ptr : S.oddPointers) { - changed |= oddPointers.insert(ptr).second; - } - return changed; - } - - bool remove(const Pointer& ptr) { - if(isOffsetValid(ptr.offset)) { - return pointers.unset(getPosition(ptr.target, ptr.offset)); - } - return oddPointers.erase(ptr) != 0; - } - - bool remove(PSNode *target, Offset offset) { - return remove(Pointer(target,offset)); - } - - bool removeAny(PSNode *target) { - bool changed = false; - size_t position = getNodePosition(target); - for(size_t i = position; i < position + 64; i++) { - changed |= pointers.unset(i); - } - auto it = oddPointers.begin(); - while(it != oddPointers.end()) { - if(it->target == target) { - it = oddPointers.erase(it); - changed = true; - } - else { - it++; - } - } - return changed; - } - - void clear() { - pointers.reset(); - oddPointers.clear(); - } - - bool pointsTo(const Pointer& ptr) const { - if(isOffsetValid(ptr.offset)) { - return pointers.get(getPosition(ptr.target,ptr.offset)); - } - return oddPointers.find(ptr) != oddPointers.end(); - } - - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) - || pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - size_t position = getNodePosition(target); - for(size_t i = position; i < position + 64; i++) { - if(pointers.get(i)) - return true; - } - for (const auto& ptr : oddPointers) { - if (ptr.target == target) - return true; - } - return false; - } - - bool isSingleton() const { - return (pointers.size() == 1 && oddPointers.size() == 0) - || (pointers.size() == 0 && oddPointers.size() == 1); - } - - bool empty() const { - return pointers.size() == 0 - && oddPointers.size() == 0; - } - - size_t count(const Pointer& ptr) const { - return pointsTo(ptr); - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { - return pointsToTarget(UNKNOWN_MEMORY); - } - - bool hasNull() const { - return pointsToTarget(NULLPTR); - } - - bool hasInvalidated() const { - return pointsToTarget(INVALIDATED); - } - - size_t size() const { - return pointers.size() + oddPointers.size(); - } - - void swap(AlignedSmallOffsetsPointsToSet& rhs) { - pointers.swap(rhs.pointers); - oddPointers.swap(rhs.oddPointers); - } - - size_t overflowSetSize() const { - return oddPointers.size(); - } - - static unsigned int getMultiplier() { - return multiplier; - } - - //iterates over the bitvector first, then over the set - class const_iterator { - - typename ADT::SparseBitvector::const_iterator bitvector_it; - typename ADT::SparseBitvector::const_iterator bitvector_end; - typename std::set::const_iterator set_it; - bool secondContainer; - - const_iterator(const ADT::SparseBitvector& pointers, const std::set& oddPointers, bool end = false) - : bitvector_it(end ? pointers.end() : pointers.begin()), - bitvector_end(pointers.end()), - set_it(end ? oddPointers.end() : oddPointers.begin()), - secondContainer(end) { - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } - - public: - const_iterator& operator++() { - if(!secondContainer) { - bitvector_it++; - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } else { - set_it++; - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - if(!secondContainer) { - size_t offsetPosition = (*bitvector_it % 64); - size_t nodeID = ((*bitvector_it - offsetPosition) / 64) + 1; - return offsetPosition == 63 ? Pointer(idVector[nodeID - 1], Offset::UNKNOWN) : Pointer(idVector[nodeID - 1], offsetPosition * multiplier); - } - return *set_it; - } - - bool operator==(const const_iterator& rhs) const { - return bitvector_it == rhs.bitvector_it - && set_it == rhs.set_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class AlignedSmallOffsetsPointsToSet; - }; - - const_iterator begin() const { return const_iterator(pointers, oddPointers); } - const_iterator end() const { return const_iterator(pointers, oddPointers, true /* end */); } - - friend class const_iterator; -}; - -} // namespacce pta -} // namespace dg - -#endif /* ALIGNEDOFFSETSPOINTSTOSET_H */ diff --git a/include/dg/PointerAnalysis/PointsToSets/LookupTable.h b/include/dg/PointerAnalysis/PointsToSets/LookupTable.h deleted file mode 100644 index 2a961d230..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/LookupTable.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef DG_PTSETS_LOOKUPTABLE_H_ -#define DG_PTSETS_LOOKUPTABLE_H_ - -#include -#include - -#ifdef HAVE_TSL_HOPSCOTCH -#include "dg/ADT/HashMap.h" -#else -#include "dg/ADT/Map.h" -#endif - -#include "dg/PointerAnalysis/Pointer.h" - -namespace dg { - -class PointerIDLookupTable { -public: - using IDTy = size_t; - using Pointer = pta::Pointer; - using PSNode = pta::PSNode; -#if defined(HAVE_TSL_HOPSCOTCH) || (__clang__) - using PtrToIDMap = dg::HashMap>; -#else - // we create the lookup table statically and there is a bug in GCC - // that breaks statically created std::unordered_map. - // So if we have not Hopscotch map, use std::map instead. - using PtrToIDMap = dg::Map>; -#endif - - // this will get a new ID for the pointer if not present - IDTy getOrCreate(const Pointer& ptr) { - auto res = get(ptr); - if (res != 0) - return res; - - _idToPtr.push_back(ptr); - res = _idToPtr.size(); -#ifndef NDEBUG - bool r = -#endif - _ptrToID[ptr.target].put(ptr.offset, res); - - assert(r && "Duplicated ID!"); - assert(get(res) == ptr); - assert(res == get(ptr)); - assert(res > 0 && "ID must always be greater than 0"); - return res; - } - - IDTy get(const Pointer& ptr) const { - auto it = _ptrToID.find(ptr.target); - if (it == _ptrToID.end()) { - return 0; // invalid ID - } - auto it2 = it->second.find(ptr.offset); - if (it2 == it->second.end()) - return 0; - return it2->second; - } - - const Pointer& get(IDTy id) const { - assert(id - 1 < _idToPtr.size()); - return _idToPtr[id - 1]; - } - -private: - // PSNode -> (Offset -> id) - // Not space efficient, but we need mainly the time efficiency here... - // NOTE: unfortunately, atm, we cannot use the id of the target for hashing - // because it would break repeated runs of the analysis - // as multiple graphs will contain nodes with the same id - // (and resetting the state is really painful, I tried that, - // but just didn't succeed). - PtrToIDMap _ptrToID; - std::vector _idToPtr; //starts from 0 (pointer = idVector[id - 1]) -}; - -/* -class PointerIDLookupTable { -public: - using IDTy = size_t; - using Pointer = pta::Pointer; - - // this will get a new ID for the pointer if not present - IDTy getOrCreate(const Pointer& ptr) { - auto it = _ptrToID.find(ptr); - if (it != _ptrToID.end()) { - return it->second; - } - auto res = _ptrToID.size() + 1; - assert(_idToPtr.size() == res - 1); - - _idToPtr.push_back(ptr); - _ptrToID.emplace(ptr, _ptrToID.size() + 1); - - assert(get(res) == ptr); - assert(res == get(ptr)); - return res; - - //bool r = _ptrToID.put(ptr, ids.size() + 1); - //assert(r && "Duplicated ID!"); - //return ids.size() + 1; - } - - IDTy get(const Pointer& ptr) const { - auto it = _ptrToID.find(ptr); - if (it != _ptrToID.end()) { - return it->second; - } - return 0; // invalid ID - } - - const Pointer& get(IDTy id) const { - assert(id - 1 < _idToPtr.size()); - return _idToPtr[id - 1]; - } - -private: - std::map _ptrToID; - std::vector _idToPtr; //starts from 0 (pointer = idVector[id - 1]) -}; -*/ - -} // namespace dg - -#endif diff --git a/include/dg/PointerAnalysis/PointsToSets/OffsetsSetPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/OffsetsSetPointsToSet.h deleted file mode 100644 index 34450bf08..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/OffsetsSetPointsToSet.h +++ /dev/null @@ -1,229 +0,0 @@ -#ifndef ORIGINALPOINTSTOSET_H -#define ORIGINALPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include - -namespace dg { -namespace pta { - -// declare PSNode -class PSNode; - -class OffsetsSetPointsToSet { - // each pointer is a pair (PSNode *, {offsets}), - // so we represent them coinciesly this way - using ContainerT = std::map; - ContainerT pointers; - - bool addWithUnknownOffset(PSNode *target) { - auto it = pointers.find(target); - if (it != pointers.end()) { - // we already had that offset? - if (it->second.get(Offset::UNKNOWN)) - return false; - - // get rid of other offsets and keep - // only the unknown offset - it->second.reset(); - it->second.set(Offset::UNKNOWN); - return true; - } - - return !pointers[target].set(Offset::UNKNOWN); - } - -public: - OffsetsSetPointsToSet() = default; - OffsetsSetPointsToSet(std::initializer_list elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - if (off.isUnknown()) - return addWithUnknownOffset(target); - - auto it = pointers.find(target); - if (it == pointers.end()) { - pointers.emplace_hint(it, target, *off); - return true; - } else { - if (it->second.get(Offset::UNKNOWN)) - return false; - else { - // the set will return the previous value - // of the bit, so that means false if we are - // setting a new value - return !it->second.set(*off); - } - } - } - - bool add(const Pointer& ptr) { - return add(ptr.target, ptr.offset); - } - - // union (unite S into this set) - bool add(const OffsetsSetPointsToSet& S) { - bool changed = false; - for (auto& it : S.pointers) { - changed |= pointers[it.first].set(it.second); - } - return changed; - } - - bool add(std::initializer_list elems) { - bool changed = false; - for (const auto& e : elems) { - changed |= add(e); - } - return changed; - } - - bool remove(const Pointer& ptr) { - return remove(ptr.target, ptr.offset); - } - - /// - // Remove pointer to this target with this offset. - // This is method really removes the pair - // (target, off) even when the off is unknown - bool remove(PSNode *target, Offset offset) { - auto it = pointers.find(target); - if (it == pointers.end()) { - return false; - } - - return it->second.unset(*offset); - } - - /// - // Remove pointers pointing to this target - bool removeAny(PSNode *target) { - auto it = pointers.find(target); - if (it == pointers.end()) { - return false; - } - - pointers.erase(it); - return true; - } - - void clear() { pointers.clear(); } - - bool pointsTo(const Pointer& ptr) const { - auto it = pointers.find(ptr.target); - if (it == pointers.end()) - return false; - return it->second.get(*ptr.offset); - } - - // points to the pointer or the the same target - // with unknown offset? Note: we do not count - // unknown memory here... - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) || - pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - return pointers.find(target) != pointers.end(); - } - - bool isSingleton() const { - return pointers.size() == 1; - } - - bool empty() const { return pointers.empty(); } - - size_t count(const Pointer& ptr) const { - auto it = pointers.find(ptr.target); - if (it != pointers.end()) { - return it->second.get(*ptr.offset); - } - - return 0; - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { return pointsToTarget(UNKNOWN_MEMORY); } - bool hasNull() const { return pointsToTarget(NULLPTR); } - bool hasInvalidated() const { return pointsToTarget(INVALIDATED); } - - size_t size() const { - size_t num = 0; - for (auto& it : pointers) { - num += it.second.size(); - } - - return num; - } - - void swap(OffsetsSetPointsToSet& rhs) { pointers.swap(rhs.pointers); } - - class const_iterator { - typename ContainerT::const_iterator container_it; - typename ContainerT::const_iterator container_end; - typename ADT::SparseBitvector::const_iterator innerIt; - - const_iterator(const ContainerT& cont, bool end = false) - : container_it(end ? cont.end() : cont.begin()), container_end(cont.end()) { - if (container_it != container_end) { - innerIt = container_it->second.begin(); - } - } - public: - const_iterator& operator++() { - ++innerIt; - if (innerIt == container_it->second.end()) { - ++container_it; - if (container_it != container_end) - innerIt = container_it->second.begin(); - else - innerIt = ADT::SparseBitvector::const_iterator(); - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - return Pointer(container_it->first, *innerIt); - } - - bool operator==(const const_iterator& rhs) const { - return container_it == rhs.container_it && innerIt == rhs.innerIt; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class OffsetsSetPointsToSet; - }; - - const_iterator begin() const { return const_iterator(pointers); } - const_iterator end() const { return const_iterator(pointers, true /* end */); } - - friend class const_iterator; -}; - -} // namespace pta -} // namespace dg - - -#endif /* ORIGINALPOINTSTOSET_H */ - diff --git a/include/dg/PointerAnalysis/PointsToSets/PointerIdPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/PointerIdPointsToSet.h deleted file mode 100644 index 756cba266..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/PointerIdPointsToSet.h +++ /dev/null @@ -1,219 +0,0 @@ -#ifndef SINGLEBITVECTORPOINTSTOSET_H -#define SINGLEBITVECTORPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" -#include "LookupTable.h" - -#include -#include -#include - -namespace dg { -namespace pta { - -class PSNode; - -class PointerIdPointsToSet { - static PointerIDLookupTable lookupTable; - -#if defined(HAVE_TSL_HOPSCOTCH) || (__clang__) - using PointersT = ADT::SparseBitvectorHashImpl; -#else - using PointersT = ADT::SparseBitvector; -#endif - PointersT pointers; - - //if the pointer doesn't have ID, it's assigned one - size_t getPointerID(const Pointer& ptr) const { - return lookupTable.getOrCreate(ptr); - } - - const Pointer& getPointer(size_t id) const { - return lookupTable.get(id); - } - - bool addWithUnknownOffset(PSNode* node) { - auto ptrid = getPointerID({node, Offset::UNKNOWN}); - if (!pointers.get(ptrid)) { - removeAny(node); - return !pointers.set(ptrid); - } - return false; // we already had it - } - -public: - PointerIdPointsToSet() = default; - explicit PointerIdPointsToSet(const std::initializer_list& elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - return add(Pointer(target,off)); - } - - bool add(const Pointer& ptr) { - if(has({ptr.target, Offset::UNKNOWN})) { - return false; - } - if(ptr.offset.isUnknown()) { - return addWithUnknownOffset(ptr.target); - } - return !pointers.set(getPointerID(ptr)); - } - - template - bool add(const ContainerTy& C) { - bool changed = false; - for (const auto& ptr : C) - changed |= add(ptr); - return changed; - } - - bool add(const PointerIdPointsToSet& S) { - return pointers.set(S.pointers); - } - - bool remove(const Pointer& ptr) { - return pointers.unset(getPointerID(ptr)); - } - - bool remove(PSNode *target, Offset offset) { - return remove(Pointer(target,offset)); - } - - bool removeAny(PSNode *target) { - decltype(pointers) tmp; - tmp.reserve(pointers.size()); - bool removed = false; - for (const auto& ptrID : pointers) { - if (lookupTable.get(ptrID).target != target) { - tmp.set(ptrID); - } else { - removed = true; - } - } - - tmp.swap(pointers); - - return removed; - } - - void clear() { - pointers.reset(); - } - - bool pointsTo(const Pointer& ptr) const { - return pointers.get(getPointerID(ptr)); - } - - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) - || pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - for (auto ptrid : pointers) { - auto& ptr = getPointer(ptrid); - if (ptr.target == target) { - return true; - } - } - return false; - } - - bool isSingleton() const { - return pointers.size() == 1; - } - - bool empty() const { - return pointers.empty(); - } - - size_t count(const Pointer& ptr) const { - return pointsTo(ptr); - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { - return pointsToTarget(UNKNOWN_MEMORY); - } - - bool hasNull() const { - return pointsToTarget(NULLPTR); - - } - - bool hasNullWithOffset() const { - for (auto ptrid : pointers) { - auto& ptr = getPointer(ptrid); - if (ptr.target == NULLPTR && *ptr.offset != 0) { - return true; - } - } - - return false; - } - - bool hasInvalidated() const { - return pointsToTarget(INVALIDATED); - } - - size_t size() const { - return pointers.size(); - } - - void swap(PointerIdPointsToSet& rhs) { - pointers.swap(rhs.pointers); - } - - class const_iterator { - - typename PointersT::const_iterator container_it; - - const_iterator(const PointersT& pointers, bool end = false) : - container_it(end ? pointers.end() : pointers.begin()) {} - - public: - const_iterator& operator++() { - container_it++; - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - return Pointer(lookupTable.get(*container_it)); - } - - bool operator==(const const_iterator& rhs) const { - return container_it == rhs.container_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class PointerIdPointsToSet; - }; - - const_iterator begin() const { return const_iterator(pointers); } - const_iterator end() const { return const_iterator(pointers, true /* end */); } - - friend class const_iterator; -}; - -} // namespace pta -} // namespace dg - -#endif /* SINGLEBITVECTORPOINTSTOSET_H */ diff --git a/include/dg/PointerAnalysis/PointsToSets/SeparateOffsetsPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/SeparateOffsetsPointsToSet.h deleted file mode 100644 index 01d3cabac..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/SeparateOffsetsPointsToSet.h +++ /dev/null @@ -1,193 +0,0 @@ -#ifndef SEPARATEOFFSETSPOINTSTOSET_H -#define SEPARATEOFFSETSPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include -#include - -namespace dg { -namespace pta { - -class PSNode; - -class SeparateOffsetsPointsToSet { - - ADT::SparseBitvector nodes; - ADT::SparseBitvector offsets; - static std::map ids; //nodes are numbered 1, 2, ... - static std::vector idVector; //starts from 0 (node = idVector[id - 1]) - - //if the node doesn't have ID, it is assigned one - size_t getNodeID(PSNode *node) const { - auto it = ids.find(node); - if(it != ids.end()) { - return it->second; - } - idVector.push_back(node); - return ids.emplace_hint(it, node, ids.size() + 1)->second; - } - -public: - SeparateOffsetsPointsToSet() = default; - SeparateOffsetsPointsToSet(std::initializer_list elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - if(offsets.get(Offset::UNKNOWN)) { - return !nodes.set(getNodeID(target)); - } - if(off.isUnknown()) { - offsets.reset(); - } - bool changed = !nodes.set(getNodeID(target)); - return !offsets.set(*off) || changed; - } - - bool add(const Pointer& ptr) { - return add(ptr.target, ptr.offset); - } - - bool add(const SeparateOffsetsPointsToSet& S) { - bool changed = nodes.set(S.nodes); - return offsets.set(S.offsets) || changed; - } - - bool remove(__attribute__((unused)) const Pointer& ptr) { - abort(); - } - - bool remove(__attribute__((unused))PSNode *target, __attribute__((unused)) Offset offset) { - abort(); - } - - bool removeAny(__attribute__((unused)) PSNode *target) { - abort(); - } - - void clear() { - nodes.reset(); - offsets.reset(); - } - - bool pointsTo(const Pointer& ptr) const { - return nodes.get(getNodeID(ptr.target)) && offsets.get(*ptr.offset); - } - - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) - || pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - return (nodes.size() == 1 || offsets.size() == 1) - && pointsTo(ptr); - } - - bool pointsToTarget(PSNode *target) const { - return nodes.get(getNodeID(target)); - } - - bool isSingleton() const { - return nodes.size() == 1 && offsets.size() == 1; - } - - bool empty() const { - return nodes.empty() && offsets.empty(); - } - - size_t count(const Pointer& ptr) const { - return pointsTo(ptr); - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { - return pointsToTarget(UNKNOWN_MEMORY); - } - - bool hasNull() const { - return pointsToTarget(NULLPTR); - - } - - bool hasInvalidated() const { - return pointsToTarget(INVALIDATED); - } - - size_t size() const { - return nodes.size() * offsets.size(); - } - - void swap(SeparateOffsetsPointsToSet& rhs) { - nodes.swap(rhs.nodes); - offsets.swap(rhs.offsets); - } - - //iterates through all the possible combinations of nodes and their offsets stored in this points-to set - class const_iterator { - - typename ADT::SparseBitvector::const_iterator nodes_it; - typename ADT::SparseBitvector::const_iterator nodes_end; - typename ADT::SparseBitvector::const_iterator offsets_it; - typename ADT::SparseBitvector::const_iterator offsets_begin; - typename ADT::SparseBitvector::const_iterator offsets_end; - - const_iterator(const ADT::SparseBitvector& nodes, const ADT::SparseBitvector& offsets, bool end = false) : - nodes_it(end ? nodes.end() : nodes.begin()), - nodes_end(nodes.end()), - offsets_it(offsets.begin()), - offsets_begin(offsets.begin()), - offsets_end(offsets.end()) { - if(nodes_it == nodes_end) { - offsets_it = offsets_end; - } - } - - public: - const_iterator& operator++() { - offsets_it++; - if(offsets_it == offsets_end) { - nodes_it++; - if(nodes_it != nodes_end) { - offsets_it = offsets_begin; - } - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - return Pointer(idVector[*nodes_it - 1], *offsets_it); - } - - bool operator==(const const_iterator& rhs) const { - return nodes_it == rhs.nodes_it - && offsets_it == rhs.offsets_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class SeparateOffsetsPointsToSet; - }; - - const_iterator begin() const { return const_iterator(nodes, offsets); } - const_iterator end() const { return const_iterator(nodes, offsets, true /* end */); } - - friend class const_iterator; -}; - -} // namespace pta -} // namespace dg - -#endif /* SEPARATEOFFSETSPOINTSTOSET_H */ diff --git a/include/dg/PointerAnalysis/PointsToSets/SimplePointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/SimplePointsToSet.h deleted file mode 100644 index 18263340d..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/SimplePointsToSet.h +++ /dev/null @@ -1,156 +0,0 @@ -#ifndef SIMPLEPOINTSTOSET_H -#define SIMPLEPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include - -namespace dg { -namespace pta { - -class PSNode; -// -// We keep the implementation of this points-to set because -// it is good for comparison and regression testing -class SimplePointsToSet { - using ContainerT = std::set; - ContainerT pointers; - - bool addWithUnknownOffset(PSNode *target) { - if (has({target, Offset::UNKNOWN})) - return false; - - ContainerT tmp; - for (const auto& ptr : pointers) { - if (ptr.target != target) - tmp.insert(ptr); - } - - tmp.swap(pointers); - return pointers.insert({target, Offset::UNKNOWN}).second; - } - -public: - SimplePointsToSet() = default; - SimplePointsToSet(std::initializer_list elems) { add(elems); } - - using const_iterator = typename ContainerT::const_iterator; - - bool add(PSNode *target, Offset off) { - if (off.isUnknown()) - return addWithUnknownOffset(target); - - // if we have the same pointer but with unknown offset, - // do nothing - if (has({target, Offset::UNKNOWN})) - return false; - - return pointers.emplace(target, off).second; - } - - bool add(const Pointer& ptr) { - return add(ptr.target, ptr.offset); - } - - // make union of the two sets and store it - // into 'this' set (i.e. merge rhs to this set) - bool add(const SimplePointsToSet& rhs) { - bool changed = false; - for (const auto& ptr : rhs.pointers) { - changed |= pointers.insert(ptr).second; - } - - return changed; - } - - bool add(std::initializer_list elems) { - bool changed = false; - for (const auto& e : elems) { - changed |= add(e); - } - return changed; - } - - bool remove(const Pointer& ptr) { - return pointers.erase(ptr) != 0; - } - - /// - // Remove pointer to this target with this offset. - // This is method really removes the pair - // (target, off), even when the off is unknown - bool remove(PSNode *target, Offset offset) { - return remove(Pointer(target, offset)); - } - - /// - // Remove pointers pointing to this target - bool removeAny(PSNode *target) { - if (pointsToTarget(target)) { - SimplePointsToSet tmp; - for (const auto& ptr : pointers) { - if (ptr.target == target) { - continue; - } - tmp.add(ptr); - } - assert(tmp.size() < size()); - swap(tmp); - return true; - } - return false; - } - - void clear() { pointers.clear(); } - - bool pointsTo(const Pointer& ptr) const { - return pointers.count(ptr) > 0; - } - - // points to the pointer or the the same target - // with unknown offset? Note: we do not count - // unknown memory here... - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) || - pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - for (const auto& ptr : pointers) { - if (ptr.target == target) - return true; - } - return false; - } - - bool isSingleton() const { - return pointers.size() == 1; - } - - size_t count(const Pointer& ptr) { return pointers.count(ptr); } - size_t size() const { return pointers.size(); } - bool empty() const { return pointers.empty(); } - bool has(const Pointer& ptr) { return count(ptr) > 0; } - bool hasUnknown() const { return pointsToTarget(UNKNOWN_MEMORY); } - bool hasNull() const { return pointsToTarget(NULLPTR); } - bool hasInvalidated() const { return pointsToTarget(INVALIDATED); } - - void swap(SimplePointsToSet& rhs) { pointers.swap(rhs.pointers); } - - const_iterator begin() const { return pointers.begin(); } - const_iterator end() const { return pointers.end(); } -}; - -} // namespace pta -} // namespace dg - - -#endif /* SIMPLEPOINTSTOSET_H */ - diff --git a/include/dg/PointerAnalysis/PointsToSets/SmallOffsetsPointsToSet.h b/include/dg/PointerAnalysis/PointsToSets/SmallOffsetsPointsToSet.h deleted file mode 100644 index ff9b842b6..000000000 --- a/include/dg/PointerAnalysis/PointsToSets/SmallOffsetsPointsToSet.h +++ /dev/null @@ -1,257 +0,0 @@ -#ifndef SMALLOFFSETSPOINTSTOSET_H -#define SMALLOFFSETSPOINTSTOSET_H - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/ADT/Bitvector.h" - -#include -#include -#include -#include - -namespace dg { -namespace pta { - -class PSNode; - -class SmallOffsetsPointsToSet { - - ADT::SparseBitvector pointers; - std::set largePointers; - static std::map ids; //nodes are numbered 1,2, ... - static std::vector idVector; //starts from 0 (node = idVector[id - 1]) - - //if the node doesn't have ID, it's assigned one - size_t getNodeID(PSNode *node) const { - auto it = ids.find(node); - if(it != ids.end()) { - return it->second; - } - idVector.push_back(node); - return ids.emplace_hint(it, node, ids.size() + 1)->second; - } - - size_t getNodePosition(PSNode *node) const { - return ((getNodeID(node) - 1) * 64); - } - - size_t getPosition(PSNode *node, Offset off) const { - if(off.isUnknown()) { - return getNodePosition(node) + 63; - } - return getNodePosition(node) + *off; - } - - bool isOffsetValid(Offset off) const { - return off.isUnknown() - || *off <= 62; - } - - bool addWithUnknownOffset(PSNode *target) { - removeAny(target); - return !pointers.set(getPosition(target, Offset::UNKNOWN)); - } - -public: - SmallOffsetsPointsToSet() = default; - SmallOffsetsPointsToSet(std::initializer_list elems) { add(elems); } - - bool add(PSNode *target, Offset off) { - if(has({target, Offset::UNKNOWN})) { - return false; - } else if(off.isUnknown()) { - return addWithUnknownOffset(target); - } else if(isOffsetValid(off)) { - return !pointers.set(getPosition(target,off)); - } else { - return largePointers.emplace(target, off).second; - } - } - - bool add(const Pointer& ptr) { - return add(ptr.target, ptr.offset); - } - - bool add(const SmallOffsetsPointsToSet& S) { - bool changed = pointers.set(S.pointers); - for (const auto& ptr : S.largePointers) { - changed |= largePointers.insert(ptr).second; - } - return changed; - } - - bool remove(const Pointer& ptr) { - if(isOffsetValid(ptr.offset)) { - return pointers.unset(getPosition(ptr.target, ptr.offset)); - } - return largePointers.erase(ptr) != 0; - } - - bool remove(PSNode *target, Offset offset) { - return remove(Pointer(target,offset)); - } - - bool removeAny(PSNode *target) { - bool changed = false; - size_t position = getNodePosition(target); - for(size_t i = position; i < position + 64; i++) { - changed |= pointers.unset(i); - } - auto it = largePointers.begin(); - while(it != largePointers.end()) { - if(it->target == target) { - it = largePointers.erase(it); - changed = true; - } - else { - it++; - } - } - return changed; - } - - void clear() { - pointers.reset(); - largePointers.clear(); - } - - bool pointsTo(const Pointer& ptr) const { - if(isOffsetValid(ptr.offset)) { - return pointers.get(getPosition(ptr.target, ptr.offset)); - } - return largePointers.find(ptr) != largePointers.end(); - } - - bool mayPointTo(const Pointer& ptr) const { - return pointsTo(ptr) - || pointsTo(Pointer(ptr.target, Offset::UNKNOWN)); - } - - bool mustPointTo(const Pointer& ptr) const { - assert(!ptr.offset.isUnknown() && "Makes no sense"); - return pointsTo(ptr) && isSingleton(); - } - - bool pointsToTarget(PSNode *target) const { - size_t position = getNodePosition(target); - for(size_t i = position; i < position + 64; i++) { - if(pointers.get(i)) - return true; - } - for (const auto& ptr : largePointers) { - if (ptr.target == target) - return true; - } - return false; - } - - bool isSingleton() const { - return (pointers.size() == 1 && largePointers.size() == 0) - || (pointers.size() == 0 && largePointers.size() == 1); - } - - bool empty() const { - return pointers.size() == 0 - && largePointers.size() == 0; - } - - size_t count(const Pointer& ptr) const { - return pointsTo(ptr); - } - - bool has(const Pointer& ptr) const { - return count(ptr) > 0; - } - - bool hasUnknown() const { - return pointsToTarget(UNKNOWN_MEMORY); - } - - bool hasNull() const { - return pointsToTarget(NULLPTR); - } - - bool hasInvalidated() const { - return pointsToTarget(INVALIDATED); - } - - size_t size() const { - return pointers.size() + largePointers.size(); - } - - void swap(SmallOffsetsPointsToSet& rhs) { - pointers.swap(rhs.pointers); - largePointers.swap(rhs.largePointers); - } - - size_t overflowSetSize() const { - return largePointers.size(); - } - - //iterates over the bitvector first, then over the set - class const_iterator { - - typename ADT::SparseBitvector::const_iterator bitvector_it; - typename ADT::SparseBitvector::const_iterator bitvector_end; - typename std::set::const_iterator set_it; - bool secondContainer; - - const_iterator(const ADT::SparseBitvector& pointers, const std::set& largePointers, bool end = false) - : bitvector_it(end ? pointers.end() : pointers.begin()), - bitvector_end(pointers.end()), - set_it(end ? largePointers.end() : largePointers.begin()), - secondContainer(end) { - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } - - public: - const_iterator& operator++() { - if(!secondContainer) { - bitvector_it++; - if(bitvector_it == bitvector_end) { - secondContainer = true; - } - } else { - set_it++; - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - Pointer operator*() const { - if(!secondContainer) { - size_t offsetID = *bitvector_it % 64; - size_t nodeID = ((*bitvector_it - offsetID) / 64) + 1; - return offsetID == 63 ? Pointer(idVector[nodeID - 1], Offset::UNKNOWN) : Pointer(idVector[nodeID - 1], offsetID); - } - return *set_it; - } - - bool operator==(const const_iterator& rhs) const { - return bitvector_it == rhs.bitvector_it - && set_it == rhs.set_it; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class SmallOffsetsPointsToSet; - }; - - const_iterator begin() const { return const_iterator(pointers, largePointers); } - const_iterator end() const { return const_iterator(pointers, largePointers, true /* end */); } - - friend class const_iterator; -}; -} // namespace pta -} // namespace dg - -#endif /* SMALLOFFSETSPOINTSTOSET_H */ diff --git a/include/dg/ReadWriteGraph/DefSite.h b/include/dg/ReadWriteGraph/DefSite.h deleted file mode 100644 index 1af34faeb..000000000 --- a/include/dg/ReadWriteGraph/DefSite.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef DG_DEF_SITE_H_ -#define DG_DEF_SITE_H_ - -#include -#include -#include -#include - -#include "dg/Offset.h" -#include "dg/ADT/IntervalsList.h" - -namespace dg { -namespace dda { - -class RWNode; - -/// Take two intervals (a, a_len) and (b, b_len) where 'a' ('b', resp.) is the -// start of the interval and 'a_len' ('b_len', resp.) is the length of the -// interval and check whether their are disjunctive. -// The length can be Offset::UNKNOWN for unknown length. -// The start ('a' and 'b') must be concrete numbers. -// \return true iff intervals are disjunctive -// false iff intervals are not disjunctive -inline bool -intervalsDisjunctive(uint64_t a, uint64_t a_len, - uint64_t b, uint64_t b_len) { - assert(a != Offset::UNKNOWN && "Start of an interval is unknown"); - assert(b != Offset::UNKNOWN && "Start of an interval is unknown"); - assert(a_len > 0 && "Interval of lenght 0 given"); - assert(b_len > 0 && "Interval of lenght 0 given"); - - if (a_len == Offset::UNKNOWN) { - if (b_len == Offset::UNKNOWN) { - return false; - } else { - // b_len is concrete and a_len is unknown - // use less or equal, because we are starting - // from 0 and the bytes are distinct (e.g. 4th byte - // is on offset 3) - return (a <= b) ? false : b_len <= a - b; - } - } else if (b_len == Offset::UNKNOWN) { - return (a <= b) ? a_len <= b - a : false; - } - - // the lenghts and starts are both concrete - return ((a <= b) ? (a_len <= b - a) : (b_len <= a - b)); -} - -/// -// Take two intervals (a1, a2) and (b1, b2) -// (over non-negative whole numbers) and check -// whether they overlap (not sharply, i.e. -// if a2 == b1, then itervals already overlap) -inline bool -intervalsOverlap(uint64_t a1, uint64_t a2, - uint64_t b1, uint64_t b2) { - return !intervalsDisjunctive(a1, a2, b1, b2); -} - -template -struct GenericDefSite { - using NodeTy = NodeT; - - GenericDefSite(NodeT *t, - const Offset& o = Offset::UNKNOWN, - const Offset& l = Offset::UNKNOWN) - : target(t), offset(o), len(l) { - assert((o.isUnknown() || l.isUnknown() || - *o + *l > 0) && "Invalid offset and length given"); - } - - bool operator<(const GenericDefSite& oth) const { - return target == oth.target ? - (offset == oth.offset ? len < oth.len : offset < oth.offset) - : target < oth.target; - } - - bool operator==(const GenericDefSite& oth) const { - return target == oth.target && offset == oth.offset && len == oth.len; - } - - Offset end() const { - // if the offset is unknown, stretch the interval over all possible bytes - if (offset.isUnknown()) { - return Offset::UNKNOWN; - } else { - return offset + (len - 1); - } - } - - std::pair getInterval() const { - // if the offset is unknown, stretch the interval over all possible bytes - if (offset.isUnknown()) { - return {0, Offset::UNKNOWN}; - } else { - return {offset, offset + (len - 1)}; - } - } - - // what memory this node defines - NodeT *target; - // on what offset - Offset offset; - // how many bytes - Offset len; -}; - -// for compatibility until we need to change it -using DefSite = GenericDefSite; - -extern RWNode *UNKNOWN_MEMORY; - -// FIXME: change this std::set to std::map (target->offsets) -class DefSiteSet : public std::set { -public: - DefSiteSet intersect(const DefSiteSet& rhs) const { - std::map lhssites; - std::map rhssites; - - for (auto& ds : *this) { - lhssites[ds.target].add(ds.getInterval()); - } - for (auto& ds : rhs) { - rhssites[ds.target].add(ds.getInterval()); - } - - DefSiteSet retval; - - for (auto& lit : lhssites) { - auto rit = rhssites.find(lit.first); - if (rit != rhssites.end()) { - for (const auto& I : lit.second.intersectWith(rit->second)) { - retval.emplace(lit.first, I.start, I.length()); - } - } - } - - return retval; - } - - template - void add(const Container& C) { - for (auto& e : C) { - insert(e); - } - } -}; - -// FIXME: get rid of this using -using DefSiteSetT = DefSiteSet; - -// wrapper around std::set<> with few -// improvements that will be handy in our set-up -/* -class RWNodesSet { - using ContainerTy = std::set; - - ContainerTy nodes; - bool is_unknown; - -public: - RWNodesSet() : is_unknown(false) {} - - // the set contains unknown mem. location - void makeUnknown() { - nodes.clear(); - nodes.insert(UNKNOWN_MEMORY); - is_unknown = true; - } - - bool insert(RWNode *n) { - if (is_unknown) - return false; - - if (n == UNKNOWN_MEMORY) { - makeUnknown(); - return true; - } else - return nodes.insert(n).second; - } - - size_t count(RWNode *n) const { return nodes.count(n); } - size_t size() const { return nodes.size(); } - - bool isUnknown() const { return is_unknown; } - - void clear() { - nodes.clear(); - is_unknown = false; - } - - ContainerTy::iterator begin() { return nodes.begin(); } - ContainerTy::iterator end() { return nodes.end(); } - ContainerTy::const_iterator begin() const { return nodes.begin(); } - ContainerTy::const_iterator end() const { return nodes.end(); } - - ContainerTy& getNodes() { - return nodes; - }; - -}; -*/ - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/ReadWriteGraph/RWBBlock.h b/include/dg/ReadWriteGraph/RWBBlock.h deleted file mode 100644 index 027346ae4..000000000 --- a/include/dg/ReadWriteGraph/RWBBlock.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef DG_RWBBLOCK_H_ -#define DG_RWBBLOCK_H_ - -#include - -#include "dg/ReadWriteGraph/RWNode.h" -#include "dg/BBlockBase.h" - -namespace dg { -namespace dda { - -class RWBBlock; -class RWNode; - -class RWBBlock : public BBlockBase { - RWSubgraph *subgraph{nullptr}; - -public: - - using NodeT = RWNode; - - RWBBlock() = default; - RWBBlock(RWSubgraph *s) : subgraph(s) {} - - RWSubgraph *getSubgraph() { return subgraph; } - const RWSubgraph *getSubgraph() const { return subgraph; } - - /* - auto begin() -> decltype(_nodes.begin()) { return _nodes.begin(); } - auto begin() const -> decltype(_nodes.begin()) { return _nodes.begin(); } - auto end() -> decltype(_nodes.end()) { return _nodes.end(); } - auto end() const -> decltype(_nodes.end()) { return _nodes.end(); } - */ - - // Split the block before and after the given node. - // Return newly created basic blocks (there are at most two of them). - std::pair, std::unique_ptr> - splitAround(NodeT *node) { - assert(node->getBBlock() == this - && "Spliting a block on invalid node"); - - RWBBlock *withnode = nullptr; - RWBBlock *after = nullptr; - - if (getNodes().size() == 1) { - assert(*getNodes().begin() == node); - return {nullptr, nullptr}; - } - -#ifndef NDEBUG - auto old_size = getNodes().size(); - assert(old_size > 1); -#endif - unsigned num = 0; - auto it = getNodes().begin(), et = getNodes().end(); - for (; it != et; ++it) { - if (*it == node) { - break; - } - ++num; - } - - assert(it != et && "Did not find the node"); - assert(*it == node); - - ++it; - if (it != et) { - after = new RWBBlock(subgraph); - for (; it != et; ++it) { - after->append(*it); - } - } - - // truncate nodes in this block - if (num > 0) { - withnode = new RWBBlock(subgraph); - withnode->append(node); - - getNodes().resize(num); - } else { - assert(*getNodes().begin() == node); - assert(after && "Should have a suffix"); - getNodes().resize(1); - } - - assert(!withnode || withnode->size() == 1); - assert(((getNodes().size() + - (withnode ? withnode->size() : 0) + - (after ? after->size() : 0)) == old_size) - && "Bug in splitting nodes"); - - // reconnect edges - RWBBlock *bbwithsuccessors = after; - if (!bbwithsuccessors) // no suffix - bbwithsuccessors = withnode; - - assert(bbwithsuccessors); - for (auto *s : this->_successors) { - for (auto& p : s->_predecessors) { - if (p == this) { - p = bbwithsuccessors; - } - } - } - // swap this and after successors - bbwithsuccessors->_successors.swap(this->_successors); - - if (withnode) { - this->addSuccessor(withnode); - if (after) { - withnode->addSuccessor(after); - } - } else { - assert(after && "Should have a suffix"); - this->addSuccessor(after); - } - - return {std::unique_ptr(withnode), - std::unique_ptr(after)}; - } - - - bool isReturnBBlock() const { - if (auto *last = getLast()) { - return last->isRet(); - } - return false; - } - -#ifndef NDEBUG - void dump() const; -#endif - -}; - -} // namespace dda -} // namespace dg - -#endif //DG_RWBBLOCK_H_ diff --git a/include/dg/ReadWriteGraph/RWNode.h b/include/dg/ReadWriteGraph/RWNode.h deleted file mode 100644 index e35d96a6b..000000000 --- a/include/dg/ReadWriteGraph/RWNode.h +++ /dev/null @@ -1,468 +0,0 @@ -#ifndef DG_RW_NODE_H_ -#define DG_RW_NODE_H_ - -#include - -#include "DefSite.h" -#include "dg/Offset.h" -#include "dg/SubgraphNode.h" - -#include "dg/DataDependence/DataDependenceAnalysisOptions.h" - -namespace dg { -namespace dda { - -class RWNode; -class RWSubgraph; -class ReachingDefinitionsAnalysis; - -// here the types are for type-checking (optional - user can do it -// when building the graph) and for later optimizations -enum class RWNodeType { - // invalid type of node - NONE, - // these are nodes that just represent memory allocation sites - // we need to have them even in reaching definitions analysis, - // so that we can use them as targets in DefSites - ALLOC, - DYN_ALLOC, - GLOBAL, - // nodes that write the memory - STORE, - // nodes that use the memory - LOAD, - // merging information from several locations - PHI, - //// PHIs used to pass information between procedures - // PHIs on the side of procedure (formal arguments) - INARG, - OUTARG, - // PHIs on the side of call (actual arguments) - CALLIN, - CALLOUT, - // artificial use (load) - MU, - // return from the subprocedure - RETURN, - // call node - CALL, - FORK, - JOIN, - // node that may define/use memory but does - // not fall into any of these categories - // (e.g., it represents an undefined call for which we have a model) - GENERIC, - // dummy nodes - NOOP -}; - -extern RWNode *UNKNOWN_MEMORY; - -class RWBBlock; - -class RWNode : public SubgraphNode { - RWNodeType type; - bool has_address_taken{false}; - RWBBlock *bblock = nullptr; - - class DefUses { - using T = std::vector; - T defuse; - // to differentiate between empty() because nothing - // has been added yet and empty() because there are no - // definitions - bool _init{false}; - - public: - bool add(RWNode *d) { - _init = true; - for (auto x : defuse) { - if (x == d) { - return false; - } - } - defuse.push_back(d); - return true; - } - - template - bool add(const Cont& C) { - _init = true; - bool changed = false; - for (RWNode *n : C) - changed |= add(n); - return changed; - } - - bool initialized() const { return _init; } - - operator std::vector() { return defuse; } - - T::iterator begin() { return defuse.begin(); } - T::iterator end() { return defuse.end(); } - T::const_iterator begin() const { return defuse.begin(); } - T::const_iterator end() const { return defuse.end(); } - }; - -public: - - /// - /// Gathers information about the node - /// - what memory it accesses and whether it writes it or reads it. - /// - mutable struct Annotations { - // weak update - DefSiteSetT defs; - // strong update - DefSiteSetT overwrites; - // this is set of variables used in this node - DefSiteSetT uses; - - DefSiteSetT& getDefines() { return defs; } - DefSiteSetT& getOverwrites() { return overwrites; } - DefSiteSetT& getUses() { return uses; } - const DefSiteSetT& getDefines() const { return defs; } - const DefSiteSetT& getOverwrites() const { return overwrites; } - const DefSiteSetT& getUses() const { return uses; } - } annotations; - - // for invalid nodes like UNKNOWN_MEMLOC - RWNode(RWNodeType t = RWNodeType::NONE) - : SubgraphNode(0), type(t) {} - - RWNode(unsigned id, RWNodeType t = RWNodeType::NONE) - : SubgraphNode(id), type(t) {} - - RWNodeType getType() const { return type; } - - // FIXME: create a child class (RWNodeAddressTaken??) - // and move this bool there, it is not relevant for all nodes - // (from this node then can inherit alloca, etc.) - bool hasAddressTaken() const { return has_address_taken; } - void setAddressTaken() { has_address_taken = true; } - - virtual ~RWNode() = default; - -#ifndef NDEBUG - void dump() const; -#endif - - // places where this node is defined - // (so this node has non-empty uses) - // FIXME: add a getter - DefUses defuse; - - bool addDefUse(RWNode *n) { return defuse.add(n); } - - template - bool addDefUse(const C& c) { - return defuse.add(c); - } - - virtual Annotations& getAnnotations() { return annotations; } - virtual const Annotations& getAnnotations() const { return annotations; } - - DefSiteSetT& getDefines() { return getAnnotations().getDefines(); } - DefSiteSetT& getOverwrites() { return getAnnotations().getOverwrites(); } - DefSiteSetT& getUses() { return getAnnotations().getUses(); } - const DefSiteSetT& getDefines() const { return getAnnotations().getDefines(); } - const DefSiteSetT& getOverwrites() const { return getAnnotations().getOverwrites(); } - const DefSiteSetT& getUses() const { return getAnnotations().getUses(); } - - bool defines(const RWNode *target, const Offset& off = Offset::UNKNOWN) const { - // FIXME: this is not efficient implementation, - // use the ordering on the nodes - if (off.isUnknown()) { - for (const DefSite& ds : getDefines()) - if (ds.target == target) - return true; - for (const DefSite& ds : getOverwrites()) - if (ds.target == target) - return true; - } else { - for (const DefSite& ds : getDefines()) - if (ds.target == target - && off.inRange(*ds.offset, *ds.offset + *ds.len)) - return true; - - for (const DefSite& ds : getOverwrites()) - if (ds.target == target - && off.inRange(*ds.offset, *ds.offset + *ds.len)) - return true; - } - - return false; - } - - bool usesUnknown() const { - for (auto& ds : getUses()) { - if (ds.target->isUnknown()) - return true; - } - return false; - } - - bool usesOnlyGlobals() const { - for (auto& ds : getUses()) { - if (!ds.target->isGlobal()) - return false; - } - return true; - } - - // add uses to annotations of 'this' object - // (call objects can have several annotations as they are - // composed of several nodes) - void addUse(const DefSite& ds) { annotations.getUses().insert(ds); } - - void addUse(RWNode *target, - const Offset& off = Offset::UNKNOWN, - const Offset& len = Offset::UNKNOWN) { - addUse(DefSite(target, off, len)); - } - - template - void addUses(T&& u) { - for (auto& ds : u) { - annotations.getUses().insert(ds); - } - } - - // add definitions to annotations of 'this' object - // (call objects can have several annotations as they are - // composed of several nodes) - void addDef(const DefSite& ds, bool strong_update = false) { - if (strong_update) - annotations.getOverwrites().insert(ds); - else - annotations.getDefines().insert(ds); - } - - /// - // register that the node defines the memory 'target' - // at offset 'off' of length 'len', i.e. it writes - // to memory 'target' to bytes [off, off + len]. - void addDef(RWNode *target, - const Offset& off = Offset::UNKNOWN, - const Offset& len = Offset::UNKNOWN, - bool strong_update = false) { - addDef(DefSite(target, off, len), strong_update); - } - - template - void addDefs(T&& defs) { - for (auto& ds : defs) { - addDef(ds); - } - } - - void addOverwrites(RWNode *target, - const Offset& off = Offset::UNKNOWN, - const Offset& len = Offset::UNKNOWN) { - addOverwrites(DefSite(target, off, len)); - } - - void addOverwrites(const DefSite& ds) { - annotations.getOverwrites().insert(ds); - } - - bool isUnknown() const { return this == UNKNOWN_MEMORY; } - bool isUse() const { return !getUses().empty(); } - bool isDef() const { return !getDefines().empty() || !getOverwrites().empty(); } - - bool isInOut() const { return getType() == RWNodeType::INARG || - getType() == RWNodeType::OUTARG || - getType() == RWNodeType::CALLIN || - getType() == RWNodeType::CALLOUT; } - bool isPhi() const { return getType() == RWNodeType::PHI || isInOut(); } - bool isGlobal() const { return getType() == RWNodeType::GLOBAL; } - bool isCall() const { return getType() == RWNodeType::CALL; } - bool isAlloc() const { return getType() == RWNodeType::ALLOC; } - bool isAllocation() const { return isAlloc() || isDynAlloc(); } - bool isRet() const { return getType() == RWNodeType::RETURN; } - bool isDynAlloc() const; - - bool canEscape() const { - return isDynAlloc() || isGlobal() || hasAddressTaken(); - } - - const RWBBlock *getBBlock() const { return bblock; } - RWBBlock *getBBlock() { return bblock; } - void setBBlock(RWBBlock *bb) { bblock = bb; } - - friend class ReadWriteGraph; -}; - -// we may either call a properly defined function -// or a function that is undefined and we -// have just a model for it. -class RWCalledValue { - RWSubgraph *subgraph{nullptr}; - RWNode *calledValue{nullptr}; - -public: - RWCalledValue(RWSubgraph *s) : subgraph(s) {} - RWCalledValue(RWNode *c) : calledValue(c) {} - - bool callsUndefined() const { return calledValue != nullptr; } - - RWSubgraph *getSubgraph() { return subgraph; } - RWNode *getCalledValue() { return calledValue; } - const RWSubgraph *getSubgraph() const { return subgraph; } - const RWNode *getCalledValue() const { return calledValue; } - -}; - -class RWNodeCall : public RWNode { - // what this call calls? - using CalleesT = std::vector; - // PHI nodes representing defined/used memory - // by the call - using InputsT = std::vector; - using OutputsT = std::vector; - RWNode *unknownInput{nullptr}; - - CalleesT callees; - InputsT inputs; - OutputsT outputs; - - mutable bool _annotations_summarized{false}; - - // compute the overall effect of all undefined calls - // in this call and store them into the annotations - // of this node - void _summarizeAnnotation() const { - if (callees.size() > 1) { - std::vector undefined; - for (auto& cv : callees) { - if (auto *uc = cv.getCalledValue()) { - undefined.push_back(uc); - } - } - - if (undefined.size() == 1) { - annotations = undefined[0]->annotations; - } else if (undefined.size() > 1){ - auto kills = undefined[0]->annotations.overwrites.intersect( - undefined[1]->annotations.overwrites); - for (size_t i = 2; i < undefined.size(); ++i) { - kills = kills.intersect(undefined[i]->annotations.overwrites); - } - - annotations.overwrites = kills; - for (auto *u : undefined) { - annotations.defs.add(u->annotations.defs); - annotations.uses.add(u->annotations.uses); - } - } - } - _annotations_summarized = true; - } - -public: - RWNodeCall(unsigned id) : RWNode(id, RWNodeType::CALL) {} - - static RWNodeCall *get(RWNode *n) { - return n->isCall() ? - static_cast(n) : nullptr; - } - - static const RWNodeCall *get(const RWNode *n) { - return n->isCall() ? - static_cast(n) : nullptr; - } - - RWNode *getUnknownPhi() { return unknownInput; } - - RWCalledValue *getSingleCallee() { - if (callees.size() != 1) - return nullptr; - return &callees[0]; - } - - const RWCalledValue *getSingleCallee() const { - if (callees.size() != 1) - return nullptr; - return &callees[0]; - } - - RWNode *getSingleUndefined() { - auto *cv = getSingleCallee(); - return cv ? cv->getCalledValue() : nullptr; - } - - const RWNode *getSingleUndefined() const { - auto *cv = getSingleCallee(); - return cv ? cv->getCalledValue() : nullptr; - } - - bool callsOneUndefined() const { - return getSingleUndefined() != nullptr; - } - - bool callsDefined() const { - for (auto& c : callees) { - if (c.getSubgraph()) { - return true; - } - } - return false; - } - - bool callsUndefined() const { - for (auto& c : callees) { - if (c.getCalledValue()) { - return true; - } - } - return false; - } - - const CalleesT& getCallees() const { return callees; } - CalleesT& getCallees() { return callees; } - - void addCallee(const RWCalledValue& cv) { callees.push_back(cv); } - void addCallee(RWNode *n) { callees.emplace_back(n); } - void addCallee(RWSubgraph *s); - - Annotations& getAnnotations() override { - if (auto *uc = getSingleUndefined()) - return uc->annotations; - if (!_annotations_summarized) - _summarizeAnnotation(); - return annotations; - } - const Annotations& getAnnotations() const override { - if (auto *uc = getSingleUndefined()) - return uc->annotations; - if (!_annotations_summarized) - _summarizeAnnotation(); - return annotations; - } - - void addOutput(RWNode *n) { assert(n->isPhi()); outputs.push_back(n); } - const OutputsT& getOutputs() const { return outputs; } - - void addInput(RWNode *n) { - assert(n->isPhi()); - inputs.push_back(n); - } - - void addUnknownInput(RWNode *n) { - assert(unknownInput == nullptr); - assert(n->isPhi()); - inputs.push_back(n); - unknownInput = n; - } - const InputsT& getInputs() const { return inputs; } - - -#ifndef NDEBUG - void dump() const override; -#endif -}; - -} // namespace dda -} // namespace dg - -#endif // DG_RW_NODE_H_ diff --git a/include/dg/ReadWriteGraph/RWSubgraph.h b/include/dg/ReadWriteGraph/RWSubgraph.h deleted file mode 100644 index e37b2451a..000000000 --- a/include/dg/ReadWriteGraph/RWSubgraph.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef DG_READ_WRITE_SUBRAPH_H_ -#define DG_READ_WRITE_SUBRAPH_H_ - -#include -#include - -#include "RWBBlock.h" - -namespace dg { -namespace dda { - -class ReadWriteGraph; -class RWNode; - -class RWSubgraph { - // FIXME: get rid of this - //unsigned int dfsnum{1}; - - using BBlocksVecT = std::vector>; - - // iterator over the bblocks that returns the bblock, - // not the unique_ptr to the bblock - struct block_iterator : public BBlocksVecT::iterator { - using ContainedType - = std::remove_reference()->get()))>::type; - - block_iterator(const BBlocksVecT::iterator& it) : BBlocksVecT::iterator(it) {} - block_iterator(const block_iterator&) = default; - block_iterator() = default; - - ContainedType *operator*() { - return (BBlocksVecT::iterator::operator*()).get(); - }; - ContainedType *operator->() { - return ((BBlocksVecT::iterator::operator*()).get()); - }; - }; - - BBlocksVecT _bblocks; - - struct blocks_range { - BBlocksVecT& blocks; - blocks_range(BBlocksVecT& b) : blocks(b) {} - - block_iterator begin() { return block_iterator(blocks.begin()); } - block_iterator end() { return block_iterator(blocks.end()); } - }; - - // Build blocks for the nodes. If 'dce' is set to true, - // the dead code is eliminated after building the blocks. - void buildBBlocks(bool dce = false); - - friend class ReadWriteGraph; - - std::vector _callers; - - // for debugging - std::string name; - -public: - RWSubgraph() = default; - RWSubgraph(RWSubgraph&&) = default; - RWSubgraph& operator=(RWSubgraph&&) = default; - - RWNode *getRoot() { return _bblocks.front()->getFirst(); } - const RWNode *getRoot() const { return _bblocks.front()->getFirst(); } - - void setName(const std::string& nm) { name = nm; } - const std::string& getName() const { return name; } - - RWBBlock& createBBlock() { - _bblocks.emplace_back(new RWBBlock(this)); - return *_bblocks.back().get(); - } - - void splitBBlocksOnCalls(); - void addCaller(RWNode *c) { - assert(c->getType() == RWNodeType::CALL); - for (auto *tmp : _callers) { - if (tmp == c) - return; - } - _callers.push_back(c); - } - - std::vector& getCallers() { return _callers; } - const std::vector& getCallers() const { return _callers; } - - const BBlocksVecT& getBBlocks() const { return _bblocks; } - - block_iterator bblocks_begin() { return block_iterator(_bblocks.begin()); } - block_iterator bblocks_end() { return block_iterator(_bblocks.end()); } - - blocks_range bblocks() { return blocks_range(_bblocks); } - - auto size() const -> decltype(_bblocks.size()) { return _bblocks.size(); } -}; - -} // namespace dda -} // namespace dg - - - -#endif diff --git a/include/dg/ReadWriteGraph/ReadWriteGraph.h b/include/dg/ReadWriteGraph/ReadWriteGraph.h deleted file mode 100644 index e3d9f7bf6..000000000 --- a/include/dg/ReadWriteGraph/ReadWriteGraph.h +++ /dev/null @@ -1,128 +0,0 @@ -#ifndef DG_READ_WRITE_GRAPH_H_ -#define DG_READ_WRITE_GRAPH_H_ - -#include -#include - -#include "dg/BFS.h" -#include "dg/ReadWriteGraph/RWNode.h" -#include "dg/ReadWriteGraph/RWBBlock.h" -#include "dg/ReadWriteGraph/RWSubgraph.h" - -#include "dg/util/debug.h" - -namespace dg { -namespace dda { - -class ReadWriteGraph { - size_t lastNodeID{0}; - using NodesT = std::vector>; - using SubgraphsT = std::vector>; - - NodesT _nodes; - SubgraphsT _subgraphs; - RWSubgraph *_entry{nullptr}; - - // iterator over the bsubgraphs that returns the bsubgraph, - // not the unique_ptr to the bsubgraph - struct subgraph_iterator : public SubgraphsT::iterator { - using ContainedType - = std::remove_reference< - decltype(*(std::declval()->get())) - >::type; - - subgraph_iterator(const SubgraphsT::iterator& it) - : SubgraphsT::iterator(it) {} - - ContainedType *operator*() { - return (SubgraphsT::iterator::operator*()).get(); - }; - ContainedType *operator->() { - return ((SubgraphsT::iterator::operator*()).get()); - }; - }; - - struct subgraphs_range { - SubgraphsT& subgraphs; - subgraphs_range(SubgraphsT& b) : subgraphs(b) {} - - subgraph_iterator begin() { return subgraph_iterator(subgraphs.begin()); } - subgraph_iterator end() { return subgraph_iterator(subgraphs.end()); } - }; - - -public: - ReadWriteGraph() = default; - ReadWriteGraph(ReadWriteGraph&&) = default; - ReadWriteGraph& operator=(ReadWriteGraph&&) = default; - - RWSubgraph *getEntry() { return _entry; } - const RWSubgraph *getEntry() const { return _entry; } - void setEntry(RWSubgraph *e) { _entry = e; } - - void removeUselessNodes(); - - void optimize() { - removeUselessNodes(); - } - - RWNode *getNode(unsigned id) { - assert(id - 1 < _nodes.size()); - auto *n = _nodes[id - 1].get(); - assert(n->getID() == id); - return n; - } - - const RWNode *getNode(unsigned id) const { - assert(id - 1 < _nodes.size()); - auto *n = _nodes[id - 1].get(); - assert(n->getID() == id); - return n; - } - - RWNode& create(RWNodeType t) { - if (t == RWNodeType::CALL) { - _nodes.emplace_back(new RWNodeCall(++lastNodeID)); - } else { - _nodes.emplace_back(new RWNode(++lastNodeID, t)); - } - return *_nodes.back().get(); - } - - RWSubgraph& createSubgraph() { - _subgraphs.emplace_back(new RWSubgraph()); - return *_subgraphs.back().get(); - } - - // Build blocks for the nodes. If 'dce' is set to true, - // the dead code is eliminated after building the blocks. - /* - void buildBBlocks(bool dce = false) { - for (auto& s : _subgraphs) { - s->buildBBlocks(dce); - } - } - */ - - void splitBBlocksOnCalls() { - for (auto& s : _subgraphs) { - s->splitBBlocksOnCalls(); - } - } - - subgraph_iterator subgraphs_begin() { - return subgraph_iterator(_subgraphs.begin()); - } - subgraph_iterator subgraphs_end() { - return subgraph_iterator(_subgraphs.end()); - } - - subgraphs_range subgraphs() { return subgraphs_range(_subgraphs); } - - auto size() const -> decltype(_subgraphs.size()) { return _subgraphs.size(); } -}; - -} // namespace dda -} // namespace dg - -#endif // DG_READ_WRITE_GRAPH_H_ diff --git a/include/dg/SCC.h b/include/dg/SCC.h deleted file mode 100644 index bb2d5b859..000000000 --- a/include/dg/SCC.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef DG_SCC_H_ -#define DG_SCC_H_ - -#include -#include - -#include "dg/ADT/Queue.h" -#include "dg/ADT/STLHashMap.h" - -namespace dg { - -// implementation of tarjan's algorithm for -// computing strongly connected components -// for a directed graph that has a starting vertex -// from which are all other vertices reachable -template -class SCC { -public: - using SCC_component_t = std::vector; - using SCC_t = std::vector; - - SCC() {} - - // returns a vector of vectors - every inner vector - // contains the nodes contained in one SCC - SCC_t& compute(NodeT *start) { - _compute(start); - assert(stack.empty()); - - return scc; - } - - const SCC_t& getSCC() const { - return scc; - } - - SCC_component_t& operator[](unsigned idx) { - assert(idx < scc.size()); - return scc[idx]; - } - -private: - - struct NodeInfo { - unsigned dfs_id{0}; - unsigned lowpt{0}; - bool on_stack{false}; - }; - - ADT::QueueLIFO stack; - CachingHashMap _info; - unsigned index{0}; - - - // container for the strongly connected components. - SCC_t scc; - - void _compute(NodeT *n) - { - auto& info = _info[n]; - // here we using the fact that we are a friend class - // of SubgraphNode. If we would need to make this - // algorithm more generinc, we add setters/getters. - info.dfs_id = info.lowpt = ++index; - info.on_stack = true; - stack.push(n); - - for (auto *succ : n->successors()) { - auto& succ_info = _info[succ]; - if (succ_info.dfs_id == 0) { - assert(!succ_info.on_stack); - _compute(succ); - info.lowpt = std::min(info.lowpt, succ_info.lowpt); - } else if (succ_info.on_stack) { - info.lowpt = std::min(info.lowpt, succ_info.dfs_id); - } - } - - if (info.lowpt == info.dfs_id) { - SCC_component_t component; - size_t component_num = scc.size(); - - NodeT *w; - while (_info[stack.top()].dfs_id >= info.dfs_id) { - w = stack.pop(); - auto& winfo = _info[w]; - assert(winfo.on_stack == true); - winfo.on_stack = false; - component.push_back(w); - // the numbers scc_id give - // a reverse topological order - w->setSCCId(component_num); - - if (stack.empty()) - break; - } - - scc.push_back(std::move(component)); - } - } -}; - -template -class SCCCondensation { - using SCC_t = typename SCC::SCC_t; - using SCC_component_t = typename SCC::SCC_component_t; - - struct Node { - const SCC_component_t& component; - std::set _successors; - - Node(SCC_component_t& comp) : component(comp) {} - - void addSuccessor(unsigned idx) { _successors.insert(idx); } - const SCC_component_t& operator*() const { return component; } - // XXX: create iterators instead - const std::set& successors() const { return _successors; } - }; - - std::vector nodes; - -public: - Node& operator[](unsigned idx) - { - assert(idx < nodes.size()); - return nodes[idx]; - } - - void compute(SCC_t& scc) - { - // we know the size before-hand - nodes.reserve(scc.size()); - - // create the nodes in our condensation graph - for (auto& comp : scc) - nodes.push_back(Node(comp)); - - assert(nodes.size() == scc.size()); - - int idx = 0; - for (auto& comp : scc) { - for (NodeT *node : comp) { - // we can get from this component - // to the component of succ - for (NodeT *succ : node->successors()) { - unsigned succ_idx = succ->getSCCId(); - if (static_cast(succ_idx) != idx) - nodes[idx].addSuccessor(succ_idx); - } - } - - ++idx; - } - } - - SCCCondensation() = default; - SCCCondensation(SCC& S) - { - compute(S.getSCC()); - } - - SCCCondensation(SCC_t& s) - { - compute(s); - } -}; - -} // dg - -#endif diff --git a/include/dg/Slicing.h b/include/dg/Slicing.h deleted file mode 100644 index e3044910a..000000000 --- a/include/dg/Slicing.h +++ /dev/null @@ -1,327 +0,0 @@ -#ifndef DG_SLICING_H_ -#define DG_SLICING_H_ - -#include - -#include "dg/legacy/Analysis.h" -#include "dg/legacy/NodesWalk.h" -#include "dg/legacy/BFS.h" -#include "dg/ADT/Queue.h" -#include "dg/DependenceGraph.h" - -#include "dg/BBlock.h" - -namespace dg { - -// this class will go through the nodes -// and will mark the ones that should be in the slice -template -class WalkAndMark : public legacy::NodesWalk> -{ - using Queue = dg::ADT::QueueFIFO; - -public: - /// - // forward_slc makes searching the dependencies - // in forward direction instead of backward - WalkAndMark(bool forward_slc = false) - : legacy::NodesWalk( - forward_slc ? - (legacy::NODES_WALK_DD | // legacy::NODES_WALK_CD NOTE: we handle CD separately - legacy::NODES_WALK_USE | legacy::NODES_WALK_ID) : - (legacy::NODES_WALK_REV_CD | legacy::NODES_WALK_REV_DD | - legacy::NODES_WALK_USER | legacy::NODES_WALK_ID | - legacy::NODES_WALK_REV_ID) - ), - forward_slice(forward_slc) {} - - void mark(const std::set& start, uint32_t slice_id) { - WalkData data(slice_id, this, forward_slice ? &markedBlocks : nullptr); - this->walk(start, markSlice, &data); - } - - void mark(NodeT *start, uint32_t slice_id) { - WalkData data(slice_id, this, forward_slice ? &markedBlocks : nullptr); - this->walk(start, markSlice, &data); - } - - bool isForward() const { return forward_slice; } - // returns marked blocks, but only for forward slicing atm - const std::set *>& getMarkedBlocks() { return markedBlocks; } - -private: - bool forward_slice{false}; - std::set *> markedBlocks; - - - struct WalkData - { - WalkData(uint32_t si, WalkAndMark *wm, - std::set *> *mb = nullptr) - : slice_id(si), analysis(wm) - , markedBlocks(mb) - {} - - uint32_t slice_id; - WalkAndMark *analysis; - std::set *> *markedBlocks; - }; - - static void markSlice(NodeT *n, WalkData *data) - { - uint32_t slice_id = data->slice_id; - n->setSlice(slice_id); - - // when we marked a node, we need to mark even - // the basic block - if there are basic blocks - if (BBlock *B = n->getBBlock()) { - B->setSlice(slice_id); - if (data->markedBlocks) { - data->markedBlocks->insert(B); - } - - // if this node has CDs, enque them - if (data->analysis->isForward()) { - for (auto it = n->control_begin(), et = n->control_end(); - it != et; ++it) { - data->analysis->enqueue(*it); - } - - // if this node is a jump instruction, - // add also nodes that control depend on this jump - if (n == B->getLastNode()) { - for (BBlock *CD : B->controlDependence()) { - for (auto *cdnd : CD->getNodes()) { - data->analysis->enqueue(cdnd); - } - } - } - } - } - - // the same with dependence graph, if we keep a node from - // a dependence graph, we need to keep the dependence graph - if (DependenceGraph *dg = n->getDG()) { - dg->setSlice(slice_id); - if (!data->analysis->isForward()) { - // and keep also all call-sites of this func (they are - // control dependent on the entry node) - // This is correct but not so precise - fix it later. - // Now I need the correctness... - NodeT *entry = dg->getEntry(); - assert(entry && "No entry node in dg"); - data->analysis->enqueue(entry); - } - } - } -}; - -struct SlicerStatistics -{ - SlicerStatistics() - : nodesTotal(0), nodesRemoved(0), blocksRemoved(0) {} - - // total number of nodes that were checked for removing - uint64_t nodesTotal; - // total number of nodes actually removed (including the - // ones removed in blocks) - uint64_t nodesRemoved; - // number of whole blocks removed - uint32_t blocksRemoved; -}; - -template -class Slicer : legacy::Analysis -{ - uint32_t options; - uint32_t slice_id; - - std::set *> sliced_graphs; - - // slice nodes from the graph; do it recursively for call-nodes - void sliceNodes(DependenceGraph *dg, uint32_t slice_id) - { - for (auto& it : *dg) { - NodeT *n = it.second; - - if (n->getSlice() != slice_id) { - if (removeNode(n)) // do backend's specific logic - dg->deleteNode(n); - - continue; - } - - // slice subgraphs if this node is - // a call-site that is in the slice - for (DependenceGraph *sub : n->getSubgraphs()) { - // slice the subgraph if we haven't sliced it yet - if (sliced_graphs.insert(sub).second) - sliceNodes(sub, slice_id); - } - } - - // slice the global nodes - const auto& global_nodes = dg->getGlobalNodes(); - if (!global_nodes) - return; - - for (auto& it : *global_nodes.get()) { - NodeT *n = it.second; - - if (n->getSlice() != slice_id) { - if (removeNode(n)) // do backend's specific logic - dg->deleteGlobalNode(n); - continue; - } - } - } - -protected: - - // how many nodes and blocks were removed or kept - SlicerStatistics statistics; - -public: - Slicer(uint32_t opt = 0) - :options(opt), slice_id(0) {} - - SlicerStatistics& getStatistics() { return statistics; } - const SlicerStatistics& getStatistics() const { return statistics; } - - /// - // Mark nodes dependent on 'start' with 'sl_id'. - // If 'forward_slice' is true, mark the nodes depending on 'start' instead. - uint32_t mark(NodeT *start, uint32_t sl_id = 0, bool forward_slice = false) - { - if (sl_id == 0) - sl_id = ++slice_id; - - WalkAndMark wm(forward_slice); - wm.mark(start, sl_id); - - /// - // If we are performing forward slicing, - // we must do the slice executable as we now just - // marked the nodes that are data dependent on the - // slicing criterion. We do that by using these - // nodes as slicing criteria in normal backward slicing. - if (forward_slice) { - std::set inslice; - for (auto *BB : wm.getMarkedBlocks()) { - for (auto *nd : BB->getNodes()) { - if (nd->getSlice() == sl_id) { - inslice.insert(nd); - } - } - } - - // do backward slicing to make the slice executable - if (!inslice.empty()) { - WalkAndMark wm2; - wm2.mark(inslice, sl_id); - } - } - - return sl_id; - } - - // slice the graph and its subgraphs. mark needs to be called - // before this routine (otherwise everything is sliced) - uint32_t slice(DependenceGraph *dg, uint32_t sl_id = 0) - { - // first slice away bblocks that should go away - sliceBBlocks(dg, sl_id); - - // now slice the nodes from the remaining graphs - sliceNodes(dg, sl_id); - - return sl_id; - } - - // remove node from the graph - // This virtual method allows to taky an action - // when node is being removed from the graph. It can also - // disallow removing this node by returning false - virtual bool removeNode(NodeT *) { - return true; - } - - virtual bool removeBlock(BBlock *) { - return true; - } - - struct RemoveBlockData { - uint32_t sl_id; - std::set *>& blocks; - }; - - static void getBlocksToRemove(BBlock *BB, RemoveBlockData& data) - { - if (BB->getSlice() == data.sl_id) - return; - - data.blocks.insert(BB); - } - - void sliceBBlocks(BBlock *start, uint32_t sl_id) - { - // we must queue the blocks ourselves before we potentially remove them - legacy::BBlockBFS bfs(legacy::BFS_BB_CFG); - std::set *> blocks; - - RemoveBlockData data = { sl_id, blocks }; - bfs.run(start, getBlocksToRemove, data); - - for (BBlock *blk : blocks) { - // update statistics - statistics.nodesRemoved += blk->size(); - statistics.nodesTotal += blk->size(); - ++statistics.blocksRemoved; - - // call specific handlers (overriden by child class) - removeBlock(blk); - - // remove block from the graph - blk->remove(); - } - } - - // remove BBlocks that contain no node that should be in - // sliced graph - void sliceBBlocks(DependenceGraph *graph, uint32_t sl_id) - { - auto& CB = graph->getBlocks(); -#ifndef NDEBUG - uint32_t blocksNum = CB.size(); -#endif - // gather the blocks - // FIXME: we don't need two loops, just go carefully - // through the constructed blocks (keep temporary always-valid iterator) - std::set *> blocks; - for (auto& it : CB) { - if (it.second->getSlice() != sl_id) - blocks.insert(it.second); - } - - for (BBlock *blk : blocks) { - // update statistics - statistics.nodesRemoved += blk->size(); - statistics.nodesTotal += blk->size(); - ++statistics.blocksRemoved; - - // call specific handlers (overriden by child class) - if (removeBlock(blk)) { - // remove block from the graph - blk->remove(); - } - } - - assert(CB.size() + blocks.size() == blocksNum && - "Inconsistency in sliced blocks"); - } - -}; - -} // namespace dg - -#endif diff --git a/include/dg/SubgraphBase.h b/include/dg/SubgraphBase.h deleted file mode 100644 index 0e6d23bda..000000000 --- a/include/dg/SubgraphBase.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef DG_SUBGRAPHBASE_H_ -#define DG_SUBGRAPHBASE_H_ - -namespace dg { - -template -class SubgraphBase { - using BBlocksVecT = std::vector>; - - // iterator over the bblocks that returns the bblock, - // not the unique_ptr to the bblock - struct block_iterator : public BBlocksVecT::iterator { - using ContainedType - = typename std::remove_reference()->get()))>::type; - - block_iterator(const typename BBlocksVecT::iterator& it) : BBlocksVecT::iterator(it) {} - block_iterator(const block_iterator&) = default; - block_iterator() = default; - - ContainedType *operator*() { - return (BBlocksVecT::iterator::operator*()).get(); - }; - ContainedType *operator->() { - return ((BBlocksVecT::iterator::operator*()).get()); - }; - }; - - BBlocksVecT _bblocks; - - struct blocks_range { - BBlocksVecT& blocks; - blocks_range(BBlocksVecT& b) : blocks(b) {} - - block_iterator begin() { return block_iterator(blocks.begin()); } - block_iterator end() { return block_iterator(blocks.end()); } - }; - - //std::vector _callers; - - // for debugging - std::string name; - -public: - SubgraphBase() = default; - SubgraphBase(SubgraphBase&&) = default; - SubgraphBase& operator=(SubgraphBase&&) = default; - - void setName(const std::string& nm) { name = nm; } - const std::string& getName() const { return name; } - - BBlockT& createBBlock() { - _bblocks.emplace_back(new BBlockT(static_cast(this))); - return *_bblocks.back().get(); - } - - /* - void addCaller(RWNode *c) { - assert(c->getType() == RWNodeType::CALL); - for (auto *tmp : _callers) { - if (tmp == c) - return; - } - _callers.push_back(c); - } - - std::vector& getCallers() { return _callers; } - const std::vector& getCallers() const { return _callers; } - */ - - const BBlocksVecT& getBBlocks() const { return _bblocks; } - - block_iterator bblocks_begin() { return block_iterator(_bblocks.begin()); } - block_iterator bblocks_end() { return block_iterator(_bblocks.end()); } - - blocks_range bblocks() { return blocks_range(_bblocks); } - - auto size() const -> decltype(_bblocks.size()) { return _bblocks.size(); } -}; - - -} // namespace dg - -#endif // SUBGRAPHBASE_H diff --git a/include/dg/SubgraphNode.h b/include/dg/SubgraphNode.h deleted file mode 100644 index 701407ac3..000000000 --- a/include/dg/SubgraphNode.h +++ /dev/null @@ -1,427 +0,0 @@ -#ifndef SUBGRAPH_NODE_H_ -#define SUBGRAPH_NODE_H_ - -// This file defines a basis for nodes from -// PointerGraph and reaching definitions subgraph. - -#ifndef NDEBUG -#include -#endif // not NDEBUG - -#include -#include -#include -#include - -namespace dg { - -namespace pta { class PSNode; } -namespace dda {class RWNode; } - -template -class SubgraphNode { -public: - using IDType = unsigned; - using NodesVec = std::vector; - -private: - // id of the node. Every node from a graph has a unique ID; - IDType id = 0; - - // data that can an analysis store in node - // for its own needs - void *data{nullptr}; - - // data that can user store in the node - // NOTE: I considered if this way is better than - // creating subclass of PSNode and have whatever we - // need in the subclass. Since AFAIK we need just this one pointer - // at this moment, I decided to do it this way since it - // is more simple than dynamic_cast... Once we need more - // than one pointer, we can change this design. - void *user_data{nullptr}; - - // id of scc component - unsigned int scc_id{0}; - -protected: - // XXX: make those private! - NodesVec _successors; - NodesVec _predecessors; - // XXX: maybe we could use SmallPtrVector or something like that - NodesVec operands; - // nodes that use this node - NodesVec users; - - // size of the memory - size_t size{0}; - -public: - - SubgraphNode(IDType id) : id(id) {} -#ifndef NDEBUG - // in debug mode, we have virtual dump methods - // and then we want virtual dtor. Otherwise, - // we never use polymorphism for childern classes, - // so we do not need the virtual dtor. - virtual ~SubgraphNode() = default; -#endif - - IDType getID() const { return id; } - - void setSize(size_t s) { size = s; } - size_t getSize() const { return size; } - void setSCCId(unsigned id) { scc_id = id; } - unsigned getSCCId() const { return scc_id; } - - // getters & setters for analysis's data in the node - template - T* getData() { return static_cast(data); } - template - const T* getData() const { return static_cast(data); } - - template - void *setData(T *newdata) { - void *old = data; - data = static_cast(newdata); - return old; - } - - // getters & setters for user's data in the node - template - T* getUserData() { return static_cast(user_data); } - template - const T* getUserData() const { return static_cast(user_data); } - - template - void *setUserData(T *newdata) { - void *old = user_data; - user_data = static_cast(newdata); - return old; - } - - NodeT *getOperand(int idx) const { - assert(idx >= 0 && static_cast(idx) < operands.size() - && "Operand index out of range"); - - return operands[idx]; - } - - void setOperand(int idx, NodeT *nd) { - assert(idx >= 0 && static_cast(idx) < operands.size() - && "Operand index out of range"); - - operands[idx] = nd; - } - - size_t getOperandsNum() const { - return operands.size(); - } - - void removeAllOperands() { - for (auto o : operands) { - o->removeUser(static_cast(this)); - } - operands.clear(); - } - -public: - template - size_t addOperand(NodePtr node, Args&&... args) { - addOperand(node); - return addOperand(std::forward(args)...); - } - - template::type> - size_t addOperand(NodePtr n) { - static_assert(std::is_pointer::value && - std::is_base_of::value, - "Argument is not a pointer or is not derived from this " - "class."); - assert(n && "Passed nullptr as the operand"); - operands.push_back(n); - n->addUser(static_cast(this)); - assert(n->users.size() > 0); - - return operands.size(); - } - - bool hasOperand(NodeT *n) const { - for (NodeT *x : operands) { - if (x == n) { - return true; - } - } - - return false; - } - - void addSuccessor(NodeT *succ) { - assert(succ && "Passed nullptr as the successor"); - _successors.push_back(succ); - succ->_predecessors.push_back(static_cast(this)); - } - - // return const only, so that we cannot change them - // other way then addSuccessor() - const NodesVec& successors() const { return _successors; } - const NodesVec& predecessors() const { return _predecessors; } - const NodesVec& getOperands() const { return operands; } - const NodesVec& getUsers() const { return users; } - - void replaceSingleSuccessor(NodeT *succ) { - assert(succ && "Passed nullptr as the successor"); - removeSingleSuccessor(); - addSuccessor(succ); - } - - void removeSingleSuccessor() { - assert(_successors.size() == 1); - - // we need to remove this node from - // successor's predecessors - _removeThisFromSuccessorsPredecessors(_successors[0]); - - // remove the successor - _successors.clear(); - } - - - // get the successor when we know there's only one of them - NodeT *getSingleSuccessor() const { - assert(_successors.size() == 1); - return _successors.front(); - } - - // get the successor when there's only one of them, - // otherwise get null - NodeT *getSingleSuccessorOrNull() const { - if (_successors.size() == 1) - return _successors.front(); - - return nullptr; - } - - // get the predecessor when we know there's only one of them - NodeT *getSinglePredecessor() const { - assert(_predecessors.size() == 1); - return _predecessors.front(); - } - - // get the predecessor when there's only one of them, - // or get null - NodeT *getSinglePredecessorOrNull() const { - if (_predecessors.size() == 1) - return _predecessors.front(); - - return nullptr; - } - - // insert this node in PointerGraph after n - // this node must not be in any PointerGraph - void insertAfter(NodeT *n) { - assert(n && "Passed nullptr as the node"); - assert(predecessorsNum() == 0); - assert(successorsNum() == 0); - - // take over successors - _successors.swap(n->_successors); - - // make this node the successor of n - n->addSuccessor(static_cast(this)); - - // replace the reference to n in successors - for (NodeT *succ : _successors) { - for (unsigned i = 0; i < succ->predecessorsNum(); ++i) { - if (succ->_predecessors[i] == n) - succ->_predecessors[i] = static_cast(this); - } - } - } - - // insert this node in PointerGraph before n - // this node must not be in any PointerGraph - void insertBefore(NodeT *n) { - assert(n && "Passed nullptr as the node"); - assert(predecessorsNum() == 0); - assert(successorsNum() == 0); - - // take over predecessors - _predecessors.swap(n->_predecessors); - - // 'n' is a successors of this node - addSuccessor(n); - - // replace the reference to n in predecessors - for (NodeT *pred : _predecessors) { - for (unsigned i = 0; i < pred->successorsNum(); ++i) { - if (pred->_successors[i] == n) - pred->_successors[i] = static_cast(this); - } - } - } - - // insert a sequence before this node in PointerGraph - void insertSequenceBefore(std::pair& seq) { - assert(seq.first && seq.second && "Passed nullptr in the sequence"); - // the sequence must not be inserted in any PointerGraph - assert(seq.first->predecessorsNum() == 0); - assert(seq.second->successorsNum() == 0); - - // first node of the sequence takes over predecessors - // this also clears 'this->predecessors' since seq.first - // has no predecessors - _predecessors.swap(seq.first->_predecessors); - - // replace the reference to 'this' in predecessors - for (NodeT *pred : seq.first->_predecessors) { - for (unsigned i = 0; i < pred->successorsNum(); ++i) { - if (pred->_successors[i] == this) - pred->_successors[i] = seq.first; - } - } - - // this node is successors of the last node in sequence - seq.second->addSuccessor(this); - } - - void isolate() { - // Remove this node from successors of the predecessors - for (NodeT *pred : _predecessors) { - std::vector new_succs; - new_succs.reserve(pred->_successors.size()); - - for (NodeT *n : pred->_successors) { - if (n != this) - new_succs.push_back(n); - } - - new_succs.swap(pred->_successors); - } - - // remove this nodes from successors' predecessors - for (NodeT *succ : _successors) { - std::vector new_preds; - new_preds.reserve(succ->_predecessors.size()); - - for (NodeT *n : succ->_predecessors) { - if (n != this) - new_preds.push_back(n); - } - - new_preds.swap(succ->_predecessors); - } - - // Take every predecessor and connect it to every successor. - for (NodeT *pred : _predecessors) { - for (NodeT *succ : _successors) { - assert(succ != this && "Self-loop"); - pred->addSuccessor(succ); - } - } - - _successors.clear(); - _predecessors.clear(); - } - - void replaceAllUsesWith(NodeT *nd, bool removeDupl = true) { - assert(nd != this && "Replacing uses of 'this' with 'this'"); - - // Replace 'this' in every user with 'nd'. - for (NodeT *user : users) { - for (int i = 0, e = user->getOperandsNum(); i < e; ++i) { - if (user->getOperand(i) == this) { - user->setOperand(i, nd); - // register that 'nd' is now used in 'user' - nd->addUser(user); - } - } - - if (removeDupl) - user->removeDuplicitOperands(); - } - - users.clear(); - } - - size_t predecessorsNum() const { - return _predecessors.size(); - } - - size_t successorsNum() const { - return _successors.size(); - } - -#ifndef NDEBUG - virtual void dump() const { - std::cout << "SubgraphNode <" << getID(); - } - - virtual void print() const { - dump(); - std::cout << "\n"; - } - - virtual void dumpv() const { - print(); - } -#endif - -private: - - void _removeThisFromSuccessorsPredecessors(NodeT *succ) { - std::vector tmp; - tmp.reserve(succ->predecessorsNum()); - for (NodeT *p : succ->_predecessors) { - if (p != this) - tmp.push_back(p); - } - - succ->_predecessors.swap(tmp); - } - - bool removeDuplicitOperands() { - std::set ops; - bool duplicated = false; - for (auto op : getOperands()) { - if (!ops.insert(op).second) - duplicated = true; - } - - if (duplicated) { - operands.clear(); - operands.reserve(ops.size()); - // just push the new operads, - // the users should not change in this case - // (as we just remove the duplicated ones) - for (auto op : ops) - operands.push_back(op); - } - - return duplicated; - } - - void addUser(NodeT *nd) { - // do not add duplicate users - for (auto u : users) - if (u == nd) - return; - - users.push_back(nd); - } - - void removeUser(NodeT *node) { - using std::find; - NodesVec &u = users; - - auto nodeToRemove = find(u.begin(), u.end(), node); - if (nodeToRemove != u.end()) { - u.erase(nodeToRemove); - } - } -}; - -} // dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DGArgumentPair.h b/include/dg/SystemDependenceGraph/DGArgumentPair.h deleted file mode 100644 index cfdd2b79b..000000000 --- a/include/dg/SystemDependenceGraph/DGArgumentPair.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef DG_DG_ARG_PAIR_H_ -#define DG_DG_ARG_PAIR_H_ - -#include -#include "DGNode.h" - -namespace dg { -namespace sdg { - -class DGParameters; - -class DGArgumentPair : public DGElement { - friend class DGParameters; - - DGParameters& _parameters; - - DGNodeArgument _input; - DGNodeArgument _output; - - DGArgumentPair(DGParameters& p); -public: - - static DGArgumentPair* get(DGElement *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - - DGNodeArgument& getInputArgument() { return _input; } - const DGNodeArgument& getInputArgument() const { return _input; } - - DGNodeArgument& getOutputArgument() { return _output; } - const DGNodeArgument& getOutputArgument() const { return _output; } - - DGParameters& getParameters() { return _parameters; } - const DGParameters& getParameters() const { return _parameters; } -}; - -} // namespace sdg -} // namespace dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DGBBlock.h b/include/dg/SystemDependenceGraph/DGBBlock.h deleted file mode 100644 index 53e3a83d8..000000000 --- a/include/dg/SystemDependenceGraph/DGBBlock.h +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef DG_DGBBLOCK_H_ -#define DG_DGBBLOCK_H_ - -#include - -#include "DepDGElement.h" -#include "DGNode.h" - -namespace dg { -namespace sdg { - -class DependenceGraph; - -/// -// A basic block of a dependence graph. -// Basic blocks are useful even in dependence graph in order -// to cluster nodes with the same control dependence. -class DGBBlock : public DepDGElement { - friend class DependenceGraph; - using NodesTy = std::vector; - - NodesTy _nodes; - DGBBlock(DependenceGraph& g) : DepDGElement(g, DGElementType::BBLOCK) { } - -public: - static DGBBlock *get(DGElement *elem) { - return elem->getType() == DGElementType::BBLOCK ? - static_cast(elem) : nullptr; - } - - NodesTy& getNodes() { return _nodes; } - const NodesTy& getNodes() const { return _nodes; } - - void append(DGNode *n) { - assert(n && "nullptr passed as node"); - assert(n->getBBlock() == nullptr && "BBlock already set"); - _nodes.push_back(n); - n->setBBlock(this); - } - - DGNode *front() { return _nodes.front(); } - DGNode *back() { return _nodes.back(); } - const DGNode *front() const { return _nodes.front(); } - const DGNode *back() const { return _nodes.back(); } -}; - -} // namespace sdg -} // namespace dg - -#endif // DG_DGBBLOCK_H_ diff --git a/include/dg/SystemDependenceGraph/DGElement.h b/include/dg/SystemDependenceGraph/DGElement.h deleted file mode 100644 index 769df77a4..000000000 --- a/include/dg/SystemDependenceGraph/DGElement.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef DG_DG_ELEMENT_H_ -#define DG_DG_ELEMENT_H_ - -#include - -#ifndef NDEBUG -// FIXME: move this to .cpp file -#include -#endif // not NDEBUG - -namespace dg { -namespace sdg { - -enum class DGElementType { - // Invalid node - INVALID=0, - /// special elements - // Pair of arguments (input & output) - ARG_PAIR = 1, - BBLOCK, - // nodes - // NOTE: here can follow only childs of DGNode class - NODE = 3, - ND_INSTRUCTION, - ND_ARGUMENT, - ND_CALL, - ND_ARTIFICIAL -}; - - -inline const char *DGElemTypeToCString(enum DGElementType type) -{ -#define ELEM(t) case t: do {return (#t); }while(0); break; - switch(type) { - ELEM(DGElementType::INVALID) - ELEM(DGElementType::ARG_PAIR) - ELEM(DGElementType::BBLOCK) - ELEM(DGElementType::NODE) - ELEM(DGElementType::ND_INSTRUCTION) - ELEM(DGElementType::ND_ARGUMENT) - ELEM(DGElementType::ND_CALL) - ELEM(DGElementType::ND_ARTIFICIAL) - default: - assert(false && "unknown node type"); - return "Unknown type"; - }; -#undef ELEM -} - -class DependenceGraph; - -class DGElement { - unsigned _id{0}; - DGElementType _type; - DependenceGraph& _dg; - -protected: - friend class DependenceGraph; - // Only for the use in ctor. This method gets the ID of this node - // from the DependenceGraph (increasing the graph's id counter). - unsigned getNewID(DependenceGraph& g); - -public: - virtual ~DGElement() = default; - - DGElement(DependenceGraph& dg, DGElementType t); - - DGElementType getType() const { return _type; } - unsigned getID() const { return _id; } - - const DependenceGraph& getDG() const { return _dg; } - DependenceGraph& getDG() { return _dg; } - -#ifndef NDEBUG - virtual void dump() const { - std::cout << DGElemTypeToCString(getType()); - } - - // verbose dump - void dumpv() const { - dump(); - std::cout << "\n"; - } -#endif // not NDEBUG -}; - -} // namespace sdg - - -// check type of node -template bool isa(sdg::DGElement *n) { - return n->getType() == T; -} - -} // namespace dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DGNode.h b/include/dg/SystemDependenceGraph/DGNode.h deleted file mode 100644 index 4025e82d9..000000000 --- a/include/dg/SystemDependenceGraph/DGNode.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef DG_DG_NODE_H_ -#define DG_DG_NODE_H_ - -#include -#include "DepDGElement.h" - -namespace dg { -namespace sdg { - -class DGBBlock; - -class DGNode : public DepDGElement { - DGBBlock *_bblock{nullptr}; - -protected: - DGNode(DependenceGraph& g, DGElementType t); - -public: - /// - // Assign a BBlock to the node. Having BBlocks in SDG is optional, - // but usually useful (we can merge control dependencies of nodes). - void setBBlock(DGBBlock *g) { _bblock = g; } - const DGBBlock* getBBlock() const { return _bblock; } - DGBBlock* getBBlock() { return _bblock; } - - static DGNode* get(DGElement *n) { - switch(n->getType()) { - case DGElementType::ND_INSTRUCTION: - case DGElementType::ND_CALL: - case DGElementType::ND_ARGUMENT: - case DGElementType::ND_ARTIFICIAL: - return static_cast(n); - default: - return nullptr; - } - } - -#ifndef NDEBUG - void dump() const override { - std::cout << "<"<< getID() << "> "; - DGElement::dump(); - } -#endif // not NDEBUG -}; - -/// ---------------------------------------------------------------------- -// Instruction -/// ---------------------------------------------------------------------- -class DGNodeInstruction : public DGNode { -public: - DGNodeInstruction(DependenceGraph& g) - : DGNode(g, DGElementType::ND_INSTRUCTION) {} - - static DGNodeInstruction *get(DGElement *n) { - return isa(n) ? - static_cast(n) : nullptr; - } -}; - -/// ---------------------------------------------------------------------- -// Argument -/// ---------------------------------------------------------------------- -class DGNodeArgument : public DGNode { -public: - DGNodeArgument(DependenceGraph& g) : DGNode(g, DGElementType::ND_ARGUMENT) {} - - static DGNodeArgument *get(DGElement *n) { - return isa(n) ? - static_cast(n) : nullptr; - } -}; - -/// ---------------------------------------------------------------------- -// Artificial node (e.g., vararg node, noreturn node, -// unified return node, etc.) -/// ---------------------------------------------------------------------- -class DGNodeArtificial : public DGNode { -public: - DGNodeArtificial(DependenceGraph& g) - : DGNode(g, DGElementType::ND_ARTIFICIAL) {} -}; - -} // namespace sdg -} // namespace dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DGNodeCall.h b/include/dg/SystemDependenceGraph/DGNodeCall.h deleted file mode 100644 index 53a0e02fa..000000000 --- a/include/dg/SystemDependenceGraph/DGNodeCall.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef DG_DG_NODE_CALL_H_ -#define DG_DG_NODE_CALL_H_ - -#include -#include -#include "DGNode.h" -#include "DGParameters.h" - -namespace dg { -namespace sdg { - -/// ---------------------------------------------------------------------- -// Call -/// ---------------------------------------------------------------------- -class DGNodeCall : public DGNode { - // FIXME: change to vector set or smallptr set (small vector set?) - using CalleesTy = std::set; - - CalleesTy _callees; - DGParameters _parameters; - -public: - DGNodeCall(DependenceGraph& g) - : DGNode(g, DGElementType::ND_CALL), _parameters(g) {} - - static DGNodeCall *get(DGElement *n) { - return isa(n) ? - static_cast(n) : nullptr; - } - - const CalleesTy& getCallees() const { return _callees; } - bool addCallee(DependenceGraph& g); - - DGParameters& getParameters() { return _parameters; } - const DGParameters& getParameters() const { return _parameters; } -}; - -} // namespace sdg -} // namespace dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DGParameters.h b/include/dg/SystemDependenceGraph/DGParameters.h deleted file mode 100644 index dcd5eb9e2..000000000 --- a/include/dg/SystemDependenceGraph/DGParameters.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef DG_PARAMETERS_H_ -#define DG_PARAMETERS_H_ - -#include -#include -#include - -#include "DGArgumentPair.h" -#include "DGBBlock.h" - -namespace dg { -namespace sdg { - -class DependenceGraph; - -class DGParameters { - DependenceGraph& _dg; - // node representing that the function may not return - // (it terminates the program or loops forever) - // NOTE: it is owned by the _dg after creation - DGNodeArtificial *_noreturn{nullptr}; - // output argument representing the return from the function - DGNodeArtificial *_return{nullptr}; - - using ParametersContainerTy = std::vector>; - ParametersContainerTy _params; - - // wrapper around graphs iterator that unwraps the unique_ptr - struct params_iterator : public decltype(_params.begin()) { - using OrigItType = decltype(_params.begin()); - - params_iterator() = default; - params_iterator(const params_iterator& I) = default; - params_iterator(const OrigItType& I) : OrigItType(I) {} - - DGArgumentPair& operator*() { return *OrigItType::operator*().get(); } - DGArgumentPair* operator->() { return OrigItType::operator*().get(); } - }; - - class params_range { - friend class DependenceGraph; - - ParametersContainerTy& _C; - - params_range(ParametersContainerTy& C) : _C(C) {} - public: - params_iterator begin() { return params_iterator(_C.begin()); } - params_iterator end() { return params_iterator(_C.end()); } - }; - -public: - DGParameters(DependenceGraph& dg) : _dg(dg) {} - - DependenceGraph& getDG() { return _dg; } - const DependenceGraph& getDG() const { return _dg; } - - DGArgumentPair& createParameter() { - auto *nd = new DGArgumentPair(*this); - _params.emplace_back(nd); - return *nd; - } - - DGArgumentPair& getParameter(unsigned idx) { - assert(idx < _params.size()); - return *_params[idx].get(); - } - - const DGArgumentPair& getParameter(unsigned idx) const { - assert(idx < _params.size()); - return *_params[idx].get(); - } - - size_t parametersNum() const { return _params.size(); } - - params_iterator begin() { return params_iterator(_params.begin()); } - params_iterator end() { return params_iterator(_params.end()); } - - DGNode& createReturn(); - DGNode *getReturn() { return _return; } - const DGNode *getReturn() const { return _return; } - - DGNode& createNoReturn(); - DGNode *getNoReturn() { return _noreturn; } - const DGNode *getNoReturn() const { return _noreturn; } -}; - -class DGFormalParameters : public DGParameters { - friend class DependenceGraph; - // parameters are associated to this dependence graph - std::unique_ptr _vararg; - - DGFormalParameters(DependenceGraph& dg) : DGParameters(dg) {} - -public: - - DGNodeArtificial& createVarArg(); -}; - -class DGNodeCall; - -class DGActualParameters : public DGParameters { - friend class DGNodeCall; - // these parameters are associated to this call - DGNodeCall& _call; - - DGActualParameters(DGNodeCall& call); -public: - - DGNodeCall& getCall() { return _call; } - const DGNodeCall& getCall() const { return _call; } -}; - -} // namespace sdg -} // namespace dg - -#endif // DG_PARAMETERS_H_ diff --git a/include/dg/SystemDependenceGraph/DepDGElement.h b/include/dg/SystemDependenceGraph/DepDGElement.h deleted file mode 100644 index d913cd514..000000000 --- a/include/dg/SystemDependenceGraph/DepDGElement.h +++ /dev/null @@ -1,148 +0,0 @@ -#ifndef DG_DEPENDENCIES_ELEM_H_ -#define DG_DEPENDENCIES_ELEM_H_ - -#include "dg/ADT/DGContainer.h" -#include "DGElement.h" - -namespace dg { -namespace sdg { - -class DependenceGraph; - -/// -// An element of the graph that can have dependencies -// -// FIXME: split to data and control classes, -// so that e.g., basic blocks do not bear the memory dependencies. -// It is a waste of memory. -class DepDGElement : public DGElement { - using edge_iterator = EdgesContainer::iterator; - using const_edge_iterator = EdgesContainer::const_iterator; - - // nodes that use this node as operand - EdgesContainer _use_deps; - // nodes that write to memory that this node reads - EdgesContainer _memory_deps; - // control dependencies - EdgesContainer _control_deps; - - // reverse containers - EdgesContainer _rev_use_deps; - EdgesContainer _rev_memory_deps; - EdgesContainer _rev_control_deps; - - class edges_range { - friend class DepDGElement; - EdgesContainer& _C; - - edges_range(EdgesContainer& C) : _C(C) {} - public: - edge_iterator begin() { return _C.begin(); } - edge_iterator end() { return _C.end(); } - }; - - class const_edges_range { - friend class DepDGElement; - const EdgesContainer& _C; - - const_edges_range(const EdgesContainer& C) : _C(C) {} - public: - const_edge_iterator begin() const { return _C.begin(); } - const_edge_iterator end() const { return _C.end(); } - }; - - // FIXME: add data deps iterator = use + memory - // - -protected: - DepDGElement(DependenceGraph& g, DGElementType type) : DGElement(g, type) {} - -public: - - static DepDGElement *get(DGElement *elem) { - if (elem->getType() == DGElementType::BBLOCK || - elem->getType() >= DGElementType::NODE) - return static_cast(elem); - return nullptr; - } - - /// add user of this node (edge 'this'->'nd') - void addUser(DepDGElement& nd) { - _use_deps.insert(&nd); - nd._rev_use_deps.insert(this); - } - - /// this node uses nd (the edge 'nd'->'this') - void addUses(DepDGElement& nd) { - nd.addUser(*this); - } - - // this node reads values from 'nd' (the edge 'nd' -> 'this') - void addMemoryDep(DepDGElement& nd) { - _memory_deps.insert(&nd); - nd._rev_memory_deps.insert(this); - } - - // this node is control dependent on 'nd' (the edge 'nd' -> 'this') - void addControlDep(DepDGElement& nd) { - _control_deps.insert(&nd); - nd._rev_control_deps.insert(this); - } - - // this node controls 'nd' (the edge 'this' -> 'nd') - void addControls(DepDGElement& nd) { - nd.addControlDep(*this); - } - - // use dependencies - edge_iterator uses_begin() { return _use_deps.begin(); } - edge_iterator uses_end() { return _use_deps.end(); } - edge_iterator users_begin() { return _rev_use_deps.begin(); } - edge_iterator users_end() { return _rev_use_deps.end(); } - const_edge_iterator uses_begin() const { return _use_deps.begin(); } - const_edge_iterator uses_end() const { return _use_deps.end(); } - const_edge_iterator users_begin() const { return _rev_use_deps.begin(); } - const_edge_iterator users_end() const { return _rev_use_deps.end(); } - - edges_range uses() { return edges_range(_use_deps); } - const_edges_range uses() const { return const_edges_range(_use_deps); } - edges_range users() { return edges_range(_rev_use_deps); } - const_edges_range users() const { return const_edges_range(_rev_use_deps); } - - // memory dependencies - edge_iterator memdep_begin() { return _memory_deps.begin(); } - edge_iterator memdep_end() { return _memory_deps.end(); } - edge_iterator rev_memdep_begin() { return _rev_memory_deps.begin(); } - edge_iterator rev_memdep_end() { return _rev_memory_deps.end(); } - const_edge_iterator memdep_begin() const { return _memory_deps.begin(); } - const_edge_iterator memdep_end() const { return _memory_deps.end(); } - const_edge_iterator rev_memdep_begin() const { return _rev_memory_deps.begin(); } - const_edge_iterator rev_memdep_end() const { return _rev_memory_deps.end(); } - - edges_range memdep() { return edges_range(_memory_deps); } - const_edges_range memdep() const { return const_edges_range(_memory_deps); } - edges_range rev_memdep() { return edges_range(_rev_memory_deps); } - const_edges_range rev_memdep() const { return const_edges_range(_rev_memory_deps); } - - // FIXME: add datadep iterator = memdep + uses - - // control dependencies - edge_iterator control_dep_begin() { return _control_deps.begin(); } - edge_iterator control_dep_end() { return _control_deps.end(); } - edge_iterator controls_begin() { return _rev_control_deps.begin(); } - edge_iterator controls_dep_end() { return _rev_control_deps.end(); } - const_edge_iterator control_dep_begin() const { return _control_deps.begin(); } - const_edge_iterator control_dep_end() const { return _control_deps.end(); } - const_edge_iterator controls_begin() const { return _rev_control_deps.begin(); } - const_edge_iterator controls_end() const { return _rev_control_deps.end(); } - - edges_range control_deps() { return edges_range(_control_deps); } - const_edges_range control_deps() const { return const_edges_range(_control_deps); } - edges_range controls() { return edges_range(_rev_control_deps); } - const_edges_range controls() const { return const_edges_range(_rev_control_deps); } -}; - -} // namespace sdg -} // namespace dg - -#endif diff --git a/include/dg/SystemDependenceGraph/DependenceGraph.h b/include/dg/SystemDependenceGraph/DependenceGraph.h deleted file mode 100644 index 7d0194226..000000000 --- a/include/dg/SystemDependenceGraph/DependenceGraph.h +++ /dev/null @@ -1,170 +0,0 @@ -#ifndef DG_DEPENDENCE_GRAPH_H_ -#define DG_DEPENDENCE_GRAPH_H_ - -#include -#include -#include - -#include "DGNode.h" -#include "DGNodeCall.h" -#include "DGBBlock.h" -#include "DGParameters.h" - -namespace dg { -namespace sdg { - -class SystemDependenceGraph; -class DGElement; - -/// -// Dependence graph for one procedure in an system dependence graph -// (in papers refered to as Program Dependence Graph) -class DependenceGraph { - friend class SystemDependenceGraph; - friend unsigned DGElement::getNewID(DependenceGraph& g); - - unsigned _id{0}; - unsigned _lastNodeID{0}; - - // SDG to which this dependence graph belongs - SystemDependenceGraph& _sdg; - // parameters associated to this graph - DGFormalParameters _parameters; - - using NodesContainerTy = std::vector>; - using BBlocksContainerTy = std::vector>; - using CallersContainerTy = std::set; - - NodesContainerTy _nodes; - BBlocksContainerTy _bblocks; - // call nodes that call this function - CallersContainerTy _callers; - - // only SystemDependenceGraph can create new DependenceGraph's - DependenceGraph(unsigned id, SystemDependenceGraph& g) - : _id(id), _sdg(g), _parameters(*this) { assert(id > 0); } - - std::string _name; - - // wrapper around block iterator that unwraps the unique_ptr - struct bblocks_iterator : public decltype(_bblocks.begin()) { - using OrigItType = decltype(_bblocks.begin()); - - bblocks_iterator() = default; - bblocks_iterator(const bblocks_iterator& I) = default; - bblocks_iterator(const OrigItType& I) : OrigItType(I) {} - - DGBBlock* operator*() { return OrigItType::operator*().get(); } - //DependenceGraph* operator->() { return OrigItType::operator*().get(); } - }; - - class bblocks_range { - friend class DependenceGraph; - - BBlocksContainerTy& _C; - - bblocks_range(BBlocksContainerTy& C) : _C(C) {} - public: - bblocks_iterator begin() { return bblocks_iterator(_C.begin()); } - bblocks_iterator end() { return bblocks_iterator(_C.end()); } - }; - - // wrapper around nodes iterator that unwraps the unique_ptr - struct nodes_iterator : public decltype(_nodes.begin()) { - using OrigItType = decltype(_nodes.begin()); - - nodes_iterator() = default; - nodes_iterator(const nodes_iterator& I) = default; - nodes_iterator(const OrigItType& I) : OrigItType(I) {} - - DGNode* operator*() { return OrigItType::operator*().get(); } - //DependenceGraph* operator->() { return OrigItType::operator*().get(); } - }; - - class nodes_range { - friend class DependenceGraph; - - NodesContainerTy& _C; - - nodes_range(NodesContainerTy& C) : _C(C) {} - public: - nodes_iterator begin() { return nodes_iterator(_C.begin()); } - nodes_iterator end() { return nodes_iterator(_C.end()); } - }; - - unsigned getNextNodeID() { - // we could use _nodes.size(), but this is more error-prone - // as this function could not increate _nodes.size() - return ++_lastNodeID; - } - -public: - - unsigned getID() const { return _id; } - SystemDependenceGraph& getSDG() { return _sdg; } - const SystemDependenceGraph& getSDG() const { return _sdg; } - - void setName(const std::string& nm) { _name = nm; } - const std::string& getName() const { return _name; } - - // FIXME: rename to blocks() and nodes() - bblocks_range getBBlocks() { return bblocks_range(_bblocks); } - nodes_range getNodes() { return nodes_range(_nodes); } - - - // we do not have any total order on nodes or blocks in SDG, - // but sometimes we need to get "some" node/block, so add - // a getter for the first element in containers - DGBBlock* getEntryBBlock() { - return _bblocks.empty() ? nullptr : _bblocks.begin()->get(); - } - - const DGBBlock* getEntryBBlock() const { - return _bblocks.empty() ? nullptr : _bblocks.begin()->get(); - } - - DGNode* getFirstNode() { - return _nodes.empty() ? nullptr : _nodes.begin()->get(); - } - - const DGNode* getFirstNode() const { - return _nodes.empty() ? nullptr : _nodes.begin()->get(); - } - - DGNodeInstruction& createInstruction() { - auto *nd = new DGNodeInstruction(*this); - _nodes.emplace_back(nd); - return *nd; - } - - DGNodeCall& createCall() { - auto *nd = new DGNodeCall(*this); - _nodes.emplace_back(nd); - return *nd; - } - - DGNodeArtificial& createArtificial() { - auto *nd = new DGNodeArtificial(*this); - _nodes.emplace_back(nd); - return *nd; - } - - DGBBlock& createBBlock() { - _bblocks.emplace_back(new DGBBlock(*this)); - return *_bblocks.back().get(); - } - - void addCaller(DGNodeCall *n) { - _callers.insert(n); - } - - const CallersContainerTy& getCallers() const { return _callers; } - - DGFormalParameters& getParameters() { return _parameters; } - const DGFormalParameters& getParameters() const { return _parameters; } -}; - -} // namespace sdg -} // namespace dg - -#endif // DG_DEPENDENCE_GRAPH_H_ diff --git a/include/dg/SystemDependenceGraph/SystemDependenceGraph.h b/include/dg/SystemDependenceGraph/SystemDependenceGraph.h deleted file mode 100644 index 4ec3f5562..000000000 --- a/include/dg/SystemDependenceGraph/SystemDependenceGraph.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef DG_SYSTEM_DEPENDENCE_GRAPH_H_ -#define DG_SYSTEM_DEPENDENCE_GRAPH_H_ - -#include -#include -#include - -#include "dg/SystemDependenceGraph/DependenceGraph.h" - -namespace dg { -// Use the namespace sdg for now to avoid name collisions. -// When this class is finished and working, we'll remove -// the old dependence graph class and this namespace. -namespace sdg { - -class DGNode; - -class SystemDependenceGraph { - std::set _globals; - std::vector> _graphs; - DependenceGraph* _entry{nullptr}; - - // wrapper around graphs iterator that unwraps the unique_ptr - struct graphs_iterator : public decltype(_graphs.begin()) { - using OrigItType = decltype(_graphs.begin()); - - graphs_iterator() = default; - graphs_iterator(const graphs_iterator& I) = default; - graphs_iterator(const OrigItType& I) : OrigItType(I) {} - - DependenceGraph* operator*() { return OrigItType::operator*().get(); } - //DependenceGraph* operator->() { return OrigItType::operator*().get(); } - }; - -public: - DependenceGraph *getEntry() { return _entry; } - const DependenceGraph *getEntry() const { return _entry; } - void setEntry(DependenceGraph *g) { _entry = g; } - - DependenceGraph& createGraph() { - _graphs.emplace_back(new DependenceGraph(_graphs.size() + 1, *this)); - return *_graphs.back().get(); - } - - DependenceGraph& createGraph(const std::string& name) { - auto &g = createGraph(); - g.setName(name); - return g; - } - - size_t size() const { return _graphs.size(); } - - graphs_iterator begin() { return graphs_iterator(_graphs.begin()); } - graphs_iterator end() { return graphs_iterator(_graphs.end()); } -}; - -} // namespace sdg -} // namespace dg - -#endif // _DG_DEPENDENCE_GRAPH_H_ diff --git a/include/dg/legacy/Analysis.h b/include/dg/legacy/Analysis.h deleted file mode 100644 index 7f889f3ed..000000000 --- a/include/dg/legacy/Analysis.h +++ /dev/null @@ -1,86 +0,0 @@ -#ifndef DG_LEGACY_ANALYSIS_H_ -#define DG_LEGACY_ANALYSIS_H_ - -namespace dg { -namespace legacy { - -// data for analyses, stored in nodes -struct AnalysesAuxiliaryData -{ - AnalysesAuxiliaryData() - : lastwalkid(0), dfsorder(0), bfsorder(0) {} - - // last id of walk (DFS/BFS) that ran on this node - // ~~> marker if it has been processed - unsigned int lastwalkid; - - // DFS order number of the node - unsigned int dfsorder; - // BFS order number of the node - unsigned int bfsorder; -}; - -// gather statistics about a run -struct AnalysisStatistics -{ - AnalysisStatistics() - : processedBlocks(0), processedNodes(0) {}; - - uint64_t processedBlocks; - uint64_t processedNodes; - - uint64_t getProcessedBlocks() const { return processedBlocks; } - uint64_t getProcessedNodes() const { return processedNodes; } -}; - -/// -------------------------------------------------------- -// - Analyses using nodes -/// -------------------------------------------------------- -template -class Analysis -{ -public: - AnalysesAuxiliaryData& getAnalysisData(NodeT *n) - { - return n->analysisAuxData; - } - - const AnalysisStatistics& getStatistics() const - { - return statistics; - } - -protected: - AnalysisStatistics statistics; -}; - -} // namespace legacy -} // namespace dg - -namespace dg { - -// forward declaration of BBlock -template class BBlock; - -namespace legacy { - -/// -------------------------------------------------------- -// - BBlocks analysis -/// -------------------------------------------------------- -template -class BBlockAnalysis : public Analysis> -{ -public: - using BBlockPtrT = BBlock *; - - AnalysesAuxiliaryData& getAnalysisData(BBlockPtrT BB) - { - return BB->analysisAuxData; - } -}; - - -} // namespace legacy -} // namespace dg - -#endif diff --git a/include/dg/legacy/BFS.h b/include/dg/legacy/BFS.h deleted file mode 100644 index a9ce840f6..000000000 --- a/include/dg/legacy/BFS.h +++ /dev/null @@ -1,93 +0,0 @@ -#ifndef DG_LEGACY_BFS_H_ -#define DG_LEGACY_BFS_H_ - -#include "dg/legacy/NodesWalk.h" -#include "dg/ADT/Queue.h" - -namespace dg { -namespace legacy { - -enum BFSFlags { - BFS_INTERPROCEDURAL = 1 << 0, - BFS_PARAMS = 1 << 1, - BFS_CFG = 1 << 2, - BFS_REV_CFG = 1 << 3, - BFS_CD = 1 << 4, - BFS_DD = 1 << 5, - BFS_REV_CD = 1 << 6, - BFS_REV_DD = 1 << 7, - BFS_USE = 1 << 8, - BFS_USER = 1 << 9, - - BFS_BB_CFG = 1 << 10, - BFS_BB_REV_CFG = 1 << 11, - BFS_BB_POSTDOM = 1 << 12, - BFS_BB_POSTDOM_FRONTIERS = 1 << 13, - - BFS_BB_NO_CALLSITES = 1 << 14, - BFS_BB_DOM = 1 << 15, -}; - - -static inline uint32_t -convertBFSBBFlags(uint32_t flags) -{ - uint32_t ret = 0; // for BBs we always have CFG - - if (flags & BFS_INTERPROCEDURAL) - ret |= BBLOCK_WALK_INTERPROCEDURAL; - if (flags & BFS_BB_CFG) - ret |= BBLOCK_WALK_CFG; - if (flags & BFS_PARAMS) - ret |= BBLOCK_WALK_PARAMS; - if (flags & BFS_BB_POSTDOM) - ret |= BBLOCK_WALK_POSTDOM; - if (flags & BFS_BB_DOM) - ret |= BBLOCK_WALK_DOM; - if (flags & BFS_BB_NO_CALLSITES) - ret |= BBLOCK_NO_CALLSITES; - - return ret; -} - -template -class BBlockBFS : public BBlockWalk *> > -{ -public: - using BBlockPtrT = BBlock *; - - BBlockBFS(uint32_t fl = 0) - : BBlockWalk *>>(convertBFSBBFlags(fl)), - bfsorder(0), flags(fl) {} - - template - void run(BBlockPtrT entry, FuncT func, DataT data) - { - this->walk(entry, func, data); - } - - template - void operator()(BBlockPtrT entry, FuncT func, DataT data) - { - run(entry, func, data); - } - - uint32_t getFlags() const { return flags; } -protected: - /* virtual */ - void prepare(BBlockPtrT BB) - { - // set bfs order number - AnalysesAuxiliaryData& aad = this->getAnalysisData(BB); - aad.bfsorder = ++bfsorder; - } -private: - unsigned int bfsorder; - uint32_t flags; -}; - -} // namespace legacy -} // namespace dg - -#endif diff --git a/include/dg/legacy/DFS.h b/include/dg/legacy/DFS.h deleted file mode 100644 index dc5d9acc6..000000000 --- a/include/dg/legacy/DFS.h +++ /dev/null @@ -1,158 +0,0 @@ -#ifndef DG_LEGACY_DFS_H_ -#define DG_LEGACY_DFS_H_ - -#include "dg/legacy/NodesWalk.h" -#include "dg/ADT/Queue.h" - -namespace dg { -namespace legacy { - -enum DFSFlags { - DFS_INTERPROCEDURAL = 1 << 0, - DFS_PARAMS = 1 << 1, - DFS_CD = 1 << 2, - DFS_DD = 1 << 3, - DFS_REV_CD = 1 << 4, - DFS_REV_DD = 1 << 5, - DFS_USE = 1 << 6, - DFS_USER = 1 << 7, - // go through CFG edges between - // basic blocks (enqueue first - // nodes of BB successors for _every_ node) - DFS_BB_CFG = 1 << 8, - DFS_BB_REV_CFG = 1 << 9, - DFS_BB_POSTDOM = 1 << 10, - DFS_BB_POSTDOM_FRONTIERS = 1 << 11, - - DFS_BB_NO_CALLSITES = 1 << 12, -}; - - -static inline -uint32_t convertFlags(uint32_t opts) -{ - uint32_t ret = 0; - - if (opts & DFS_INTERPROCEDURAL) - ret |= NODES_WALK_INTERPROCEDURAL; - if (opts & DFS_CD) - ret |= NODES_WALK_CD; - if (opts & DFS_DD) - ret |= NODES_WALK_DD; - if (opts & DFS_REV_CD) - ret |= NODES_WALK_REV_CD; - if (opts & DFS_REV_DD) - ret |= NODES_WALK_REV_DD; - if (opts & DFS_USE) - ret |= NODES_WALK_USE; - if (opts & DFS_USER) - ret |= NODES_WALK_USER; - if (opts & DFS_BB_CFG) - ret |= NODES_WALK_BB_CFG; - if (opts & DFS_BB_REV_CFG) - ret |= NODES_WALK_BB_REV_CFG; - if (opts & DFS_BB_POSTDOM) - ret |= NODES_WALK_BB_POSTDOM; - if (opts & DFS_BB_POSTDOM_FRONTIERS) - ret |= NODES_WALK_BB_POSTDOM_FRONTIERS; - - assert(!(opts & DFS_PARAMS) && "Not implemented yet"); - assert(!(opts & DFS_INTERPROCEDURAL) && "Not implemented yet"); - assert(!(opts & DFS_BB_NO_CALLSITES) && "Not implemented yet"); - assert(!(opts & DFS_BB_POSTDOM) && "Not implemented yet"); - - return ret; -} - -template -class DFS : public NodesWalk> -{ -public: - DFS(uint32_t opts) - : NodesWalk>(convertFlags(opts)), - dfsorder(0), flags(opts) {} - - template - void run(NodeT *entry, FuncT func, DataT data) - { - this->walk(entry, func, data); - } - - template - void operator()(NodeT *entry, FuncT func, DataT data) - { - run(entry, func, data); - } - -protected: - /* virtual */ - void prepare(NodeT *BB) - { - // set dfs order number - AnalysesAuxiliaryData& aad = this->getAnalysisData(BB); - aad.dfsorder = ++dfsorder; - } -private: - unsigned int dfsorder; - uint32_t flags; -}; - - -static uint32_t inline -convertBBFlags(uint32_t flags) -{ - uint32_t ret = 0; // for BBs we always have CFG - - if (flags & DFS_INTERPROCEDURAL) - ret |= BBLOCK_WALK_INTERPROCEDURAL; - if (flags & DFS_PARAMS) - ret |= BBLOCK_WALK_PARAMS; - if (flags & DFS_BB_NO_CALLSITES) - ret |= BBLOCK_NO_CALLSITES; - if (flags & DFS_BB_CFG) - ret |= BBLOCK_WALK_CFG; - - return ret; -} - -template -class BBlockDFS : public BBlockWalk *> > -{ -public: - using BBlockPtrT = BBlock *; - - BBlockDFS(uint32_t fl = DFS_BB_CFG) - : BBlockWalk *>>(convertBBFlags(fl)), - dfsorder(0), flags(fl) {} - - template - void run(BBlockPtrT entry, FuncT func, DataT data) - { - this->walk(entry, func, data); - } - - template - void operator()(BBlockPtrT entry, FuncT func, DataT data) - { - run(entry, func, data); - } - - uint32_t getFlags() const { return flags; } -protected: - /* virtual */ - void prepare(BBlockPtrT BB) - { - // set dfs order number - AnalysesAuxiliaryData& aad = this->getAnalysisData(BB); - aad.dfsorder = ++dfsorder; - } -private: - unsigned int dfsorder; - uint32_t flags; -}; - -} // namespace legacy -} // namespace dg - -#endif diff --git a/include/dg/legacy/DataFlowAnalysis.h b/include/dg/legacy/DataFlowAnalysis.h deleted file mode 100644 index f2bc411c7..000000000 --- a/include/dg/legacy/DataFlowAnalysis.h +++ /dev/null @@ -1,168 +0,0 @@ -#ifndef DG_DATA_FLOW_ANALYSIS_H_ -#define DG_DATA_FLOW_ANALYSIS_H_ - -#include -#include - -#include "dg/legacy/Analysis.h" -#include "dg/legacy/DFS.h" - -namespace dg { -namespace legacy { - -struct DataFlowStatistics : public AnalysisStatistics { - DataFlowStatistics() - : AnalysisStatistics(), bblocksNum(0), iterationsNum(0) {} - - uint64_t bblocksNum; - uint64_t iterationsNum; - - uint64_t getBBlocksNum() const { return bblocksNum; } - uint64_t getIterationsNum() const { return iterationsNum; } -}; - -enum DataFlowAnalysisFlags { - DATAFLOW_INTERPROCEDURAL = 1 << 0, - DATAFLOW_BB_NO_CALLSITES = 1 << 1, -}; - -// ordering of nodes with respect to DFS order -// works for both nodes and blocks -template -struct DFSOrderLess -{ - bool operator()(T a, T b) const - { - return a->getDFSOrder() < b->getDFSOrder(); - } -}; - -template -class BBlockDataFlowAnalysis : public Analysis -{ -public: - BBlockDataFlowAnalysis(BBlock *entryBB, uint32_t fl = 0) - :entryBB(entryBB), flags(fl), changed(false) {} - - virtual bool runOnBlock(BBlock *BB) = 0; - - void run() - { - uint32_t flg = DFS_BB_CFG; - - assert(entryBB && "entry basic block is nullptr"); - - /* translate flags */ - if (flags & DATAFLOW_INTERPROCEDURAL) - flg |= DFS_INTERPROCEDURAL; - if (flags & DATAFLOW_BB_NO_CALLSITES) - flg |= DFS_BB_NO_CALLSITES; - - BBlockDFS DFS(flg); - DFSDataT data(blocks, changed, this); - - // we will get all the nodes using DFS - DFS.run(entryBB, dfs_proc_bb, data); - - // update statistics - statistics.bblocksNum = blocks.size(); - statistics.iterationsNum = 1; - // first run goes over each BB once - statistics.processedBlocks = statistics.bblocksNum; - - // Iterate over the nodes in dfs reverse order, it is - // usually good for reaching fixpoint. Later we can - // add options what ordering to use. - // Since we used while loop, if nothing changed after the - // first iteration (the DFS), the loop will never run - while (changed) { - changed = false; - for (auto I = blocks.rbegin(), E = blocks.rend(); - I != E; ++I) { - changed |= runOnBlock(*I); - ++statistics.processedBlocks; - } - - ++statistics.iterationsNum; - } - } - - uint32_t getFlags() const { return flags; } - - const DataFlowStatistics& getStatistics() const - { - return statistics; - } - - bool addBB(BBlock *BB) - { - changed |= runOnBlock(BB); - bool ret = blocks.insert(BB).second; - if (ret) - ++statistics.bblocksNum; - - changed |= ret; - return ret; - } - -private: - // define set of blocks to be ordered in dfs order - // FIXME if we use dfs order, then addBB does not work, - // because the BB's newly added does have dfsorder unset - // and the BlocksSet thinks it already contains it, so - // it is not added - using BlocksSetT = std::set * /*, - DFSOrderLess *>*/>; - struct DFSDataT - { - DFSDataT(BlocksSetT& b, bool& c, BBlockDataFlowAnalysis *r) - :blocks(b), changed(c), ref(r) {} - - BlocksSetT& blocks; - bool& changed; - BBlockDataFlowAnalysis *ref; - }; - - static void dfs_proc_bb(BBlock *BB, - DFSDataT& data) - { - data.changed |= data.ref->runOnBlock(BB); - data.blocks.insert(BB); - } - - BBlock *entryBB; - BlocksSetT blocks; - uint32_t flags; - bool changed; - DataFlowStatistics statistics; -}; - - -template -class DataFlowAnalysis : public BBlockDataFlowAnalysis -{ -public: - DataFlowAnalysis(BBlock *entryBB, uint32_t fl = 0) - : BBlockDataFlowAnalysis(entryBB, fl) {}; - - /* virtual */ - bool runOnBlock(BBlock *B) - { - bool changed = false; - NodeT *prev = nullptr; - - for (NodeT *n : B->getNodes()) { - changed |= runOnNode(n, prev); - prev = n; - } - - return changed; - } - - virtual bool runOnNode(NodeT *n, NodeT *prev) = 0; -}; - -} // namespace legacy -} // namespace dg - -#endif diff --git a/include/dg/legacy/NodesWalk.h b/include/dg/legacy/NodesWalk.h deleted file mode 100644 index 3225de86a..000000000 --- a/include/dg/legacy/NodesWalk.h +++ /dev/null @@ -1,389 +0,0 @@ -#ifndef DG_LEGACY_NODES_WALK_H_ -#define DG_LEGACY_NODES_WALK_H_ - -#include "dg/DGParameters.h" -#include "dg/legacy/Analysis.h" - -namespace dg { -namespace legacy { - -enum NodesWalkFlags { - // do not walk any edges, user will - // use enqueue method to decide which nodes - // will be processed - NODES_WALK_NONE_EDGES = 0, - NODES_WALK_INTERPROCEDURAL = 1 << 0, - NODES_WALK_CD = 1 << 1, - NODES_WALK_DD = 1 << 2, - NODES_WALK_REV_CD = 1 << 3, - NODES_WALK_REV_DD = 1 << 4, - NODES_WALK_USE = 1 << 5, - NODES_WALK_USER = 1 << 6, - // interference dependencies - NODES_WALK_ID = 1 << 7, - NODES_WALK_REV_ID = 1 << 8, - // Add to queue all first nodes of - // node's BB successors - NODES_WALK_BB_CFG = 1 << 9, - // Add to queue all last nodes of - // node's BB predecessors - NODES_WALK_BB_REV_CFG = 1 << 10, - NODES_WALK_BB_POSTDOM = 1 << 11, - NODES_WALK_BB_POSTDOM_FRONTIERS = 1 << 12, -}; - -// this is a base class for nodes walk, it contains -// counter. If we would add counter (even static) into -// NodesWalk itself, we'd have counter for every -// NodeT+QueueT instantiation, which we don't want to, -// because then BFS and DFS would collide -template -class NodesWalkBase : public Analysis -{ -protected: - // this counter will increase each time we run - // NodesWalk, so it can be used as an indicator - // that we queued a node in a particular run or not - static unsigned int walk_run_counter; -}; - -// counter definition -template -unsigned int NodesWalkBase::walk_run_counter = 0; - -template -class NodesWalk : public NodesWalkBase -{ -public: - NodesWalk(uint32_t opts = 0) - : options(opts) {} - - template - void walk(NodeT *entry, FuncT func, DataT data) { - walk(std::set{entry}, func, data); - } - - template - void walk(const std::set& entry, FuncT func, DataT data) - { - run_id = ++NodesWalk::walk_run_counter; - - assert(!entry.empty() && "Need entry node for traversing nodes"); - for (auto ent : entry) - enqueue(ent); - - while (!queue.empty()) { - NodeT *n = queue.pop(); - - prepare(n); - func(n, data); - - // do not try to process edges if we know - // we should not - if (options == 0) - continue; - - // add unprocessed vertices - if (options & NODES_WALK_CD) { - processEdges(n->control_begin(), n->control_end()); - // we can have control dependencies in BBlocks - processBBlockCDs(n); - } - - if (options & NODES_WALK_REV_CD) { - processEdges(n->rev_control_begin(), n->rev_control_end()); - - // we can have control dependencies in BBlocks - processBBlockRevCDs(n); - } - - if (options & NODES_WALK_DD) - processEdges(n->data_begin(), n->data_end()); - - if (options & NODES_WALK_REV_DD) - processEdges(n->rev_data_begin(), n->rev_data_end()); - - if (options & NODES_WALK_USE) - processEdges(n->use_begin(), n->use_end()); - - if (options & NODES_WALK_USER) - processEdges(n->user_begin(), n->user_end()); - - if (options & NODES_WALK_ID) - processEdges(n->interference_begin(), n->interference_end()); - - if (options & NODES_WALK_REV_ID) - processEdges(n->rev_interference_begin(), n->rev_interference_end()); - - if (options & NODES_WALK_BB_CFG) - processBBlockCFG(n); - - if (options & NODES_WALK_BB_REV_CFG) - processBBlockRevCFG(n); - - if (options & NODES_WALK_BB_POSTDOM_FRONTIERS) - processBBlockPostDomFrontieres(n); - - // FIXME interprocedural - } - } - - // push a node into queue - // This method is public so that analysis can - // push some extra nodes into queue as they want. - // They can also say that they don't want to process - // any edges and take care of pushing the right nodes - // on their own - void enqueue(NodeT *n) - { - AnalysesAuxiliaryData& aad = this->getAnalysisData(n); - - if (aad.lastwalkid == run_id) - return; - - // mark node as visited - aad.lastwalkid = run_id; - queue.push(n); - } - -protected: - // function that will be called for all the nodes, - // but is defined by the analysis framework, not - // by the analysis itself. For example it may - // assign DFS order numbers - virtual void prepare(NodeT *n) - { - (void) n; - } - -private: - template - void processEdges(IT begin, IT end) - { - for (IT I = begin; I != end; ++I) { - enqueue(*I); - } - } - - // we can have control dependencies in BBlocks - void processBBlockRevCDs(NodeT *n) - { - // push terminator nodes of all blocks that are - // control dependent - BBlock *BB = n->getBBlock(); - if (!BB) - return; - - for (BBlock *CD : BB->revControlDependence()) - enqueue(CD->getLastNode()); - } - - void processBBlockCDs(NodeT *n) - { - BBlock *BB = n->getBBlock(); - if (!BB) - return; - - for (BBlock *CD : BB->controlDependence()) - enqueue(CD->getFirstNode()); - } - - - void processBBlockCFG(NodeT *n) - { - BBlock *BB = n->getBBlock(); - if (!BB) - return; - - for (auto& E : BB->successors()) - enqueue(E.target->getFirstNode()); - } - - void processBBlockRevCFG(NodeT *n) - { - BBlock *BB = n->getBBlock(); - if (!BB) - return; - - for (BBlock *S : BB->predecessors()) - enqueue(S->getLastNode()); - } - - void processBBlockPostDomFrontieres(NodeT *n) - { - BBlock *BB = n->getBBlock(); - if (!BB) - return; - - for (BBlock *S : BB->getPostDomFrontiers()) - enqueue(S->getLastNode()); - } - - QueueT queue; - // id of particular nodes walk - unsigned int run_id; - uint32_t options; -}; - -enum BBlockWalkFlags { - // recurse into procedures - BBLOCK_WALK_INTERPROCEDURAL = 1 << 0, - // walk even through params - BBLOCK_WALK_PARAMS = 1 << 1, - // walk post-dominator tree edges - BBLOCK_WALK_POSTDOM = 1 << 2, - // walk normal CFG edges - BBLOCK_WALK_CFG = 1 << 3, - // need to go through the nodes once - // because bblocks does not keep information - // about call-sites - BBLOCK_NO_CALLSITES = 1 << 4, - // walk dominator tree edges - BBLOCK_WALK_DOM = 1 << 5, -}; - -// this is a base class for bblock walk, it contains -// counter. If we would add counter (even static) into -// NodesWalk itself, we'd have counter for every -// NodeT+QueueT instantiation, which we don't want to, -// because then BFS and DFS would collide -template -class BBlockWalkBase : public BBlockAnalysis -{ -protected: - // this counter will increase each time we run - // NodesWalk, so it can be used as an indicator - // that we queued a node in a particular run or not - static unsigned int walk_run_counter; -}; - -// counter definition -template -unsigned int BBlockWalkBase::walk_run_counter = 0; - -template -class BBlockWalk : public BBlockWalkBase -{ -public: - using BBlockPtrT = dg::BBlock *; - - BBlockWalk(uint32_t fl = BBLOCK_WALK_CFG) - : flags(fl) {} - - template - void walk(BBlockPtrT entry, FuncT func, DataT data) - { - queue.push(entry); - - // set up the value of new walk and set it to entry node - runid = ++BBlockWalk::walk_run_counter; - AnalysesAuxiliaryData& aad = this->getAnalysisData(entry); - aad.lastwalkid = runid; - - while (!queue.empty()) { - BBlockPtrT BB = queue.pop(); - - prepare(BB); - func(BB, data); - - // update statistics - ++this->statistics.processedBlocks; - - // should and can we go into subgraph? - if ((flags & BBLOCK_WALK_INTERPROCEDURAL)) { - if ((flags & BBLOCK_NO_CALLSITES) - && BB->getCallSitesNum() == 0) { - // get callsites if bblocks does not keep them - for (NodeT *n : BB->getNodes()) { - if (n->hasSubgraphs()) - BB->addCallsite(n); - } - } - - if (BB->getCallSitesNum() != 0) - queueSubgraphsBBs(BB); - } - - // queue post-dominated blocks if we should - if (flags & BBLOCK_WALK_POSTDOM) - for (BBlockPtrT S : BB->getPostDominators()) - enqueue(S); - - // queue dominated blocks - if (flags & BBLOCK_WALK_DOM) - for (BBlockPtrT S : BB->getDominators()) - enqueue(S); - - // queue sucessors of this BB - if (flags & BBLOCK_WALK_CFG) - for (auto& E : BB->successors()) - enqueue(E.target); - } - } - - uint32_t getFlags() const { return flags; } - - void enqueue(BBlockPtrT BB) - { - AnalysesAuxiliaryData& sad = this->getAnalysisData(BB); - if (sad.lastwalkid != runid) { - sad.lastwalkid = runid; - queue.push(BB); - } - } - -protected: - virtual void prepare(BBlockPtrT BB) - { - (void) BB; - } - -private: - void queueSubgraphsBBs(BBlockPtrT BB) - { - DGParameters *params; - - // iterate over call-site nodes - for (NodeT *cs : BB->getCallSites()) { - // go through parameters if desired - if ((flags & BBLOCK_WALK_PARAMS)) { - params = cs->getParameters(); - if (params) { - enqueue(params->getBBIn()); - enqueue(params->getBBOut()); - } - } - - // iterate over subgraphs in call-site node - // and put into queue entry blocks - for (auto subdg : cs->getSubgraphs()) { - // go into formal parameters if wanted - if ((flags & BBLOCK_WALK_PARAMS)) { - NodeT *entry = subdg->getEntry(); - assert(entry && "No entry node in sub dg"); - - params = entry->getParameters(); - if (params) { - enqueue(params->getBBIn()); - enqueue(params->getBBOut()); - } - } - - // queue entry BBlock - BBlockPtrT entryBB = subdg->getEntryBB(); - assert(entryBB && "No entry block in sub dg"); - enqueue(entryBB); - } - } - } - - QueueT queue; - uint32_t flags; - unsigned int runid; -}; - - -} // legacy -} // namespace dg - -#endif diff --git a/include/dg/llvm/CallGraph/CallGraph.h b/include/dg/llvm/CallGraph/CallGraph.h deleted file mode 100644 index da4c94ee0..000000000 --- a/include/dg/llvm/CallGraph/CallGraph.h +++ /dev/null @@ -1,231 +0,0 @@ -#ifndef DG_LLVM_CALLGRAPH_H_ -#define DG_LLVM_CALLGRAPH_H_ - -#include -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/PointerAnalysis/PSNode.h" -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/CallGraph/CallGraph.h" -#include "dg/ADT/Queue.h" -#include "dg/ADT/SetQueue.h" - -namespace dg { -namespace pta { - class PSNode; -} - -namespace llvmdg { - -class CallGraphImpl { -public: - using FuncVec = std::vector; - - virtual ~CallGraphImpl() = default; - - // XXX: return iterators... - virtual FuncVec functions() const = 0; - virtual FuncVec callers(const llvm::Function *) const = 0; - virtual FuncVec callees(const llvm::Function *) const = 0; - virtual bool calls(const llvm::Function*, const llvm::Function *) const = 0; -}; - -/// -/// Callgraph that re-uses the Call graph built during pointer analysis from DG -/// -class DGCallGraphImpl : public CallGraphImpl { - const GenericCallGraph& _cg; - std::map _mapping; - - const llvm::Function *getFunFromNode(PSNode *n) const { - auto f = n->getUserData(); - assert(f && "Invalid data in a node"); - return f; - } - -public: - DGCallGraphImpl(const dg::GenericCallGraph& cg) : _cg(cg) { - for (auto& it : _cg) { - _mapping[getFunFromNode(it.first)] = it.first; - } - } - - FuncVec functions() const override { - FuncVec ret; - for (auto& it : _mapping) { - ret.push_back(it.first); - } - return ret; - } - - FuncVec callers(const llvm::Function *F) const override { - FuncVec ret; - auto it = _mapping.find(F); - if (it == _mapping.end()) - return ret; - auto fnd = _cg.get(it->second); - for (auto *nd : fnd->getCallers()) { - ret.push_back(getFunFromNode(nd->getValue())); - } - return ret; - } - - FuncVec callees(const llvm::Function *F) const override { - FuncVec ret; - auto it = _mapping.find(F); - if (it == _mapping.end()) - return ret; - auto fnd = _cg.get(it->second); - for (auto *nd : fnd->getCalls()) { - ret.push_back(getFunFromNode(nd->getValue())); - } - return ret; - } - - bool calls(const llvm::Function *F, const llvm::Function *what) const override { - auto it = _mapping.find(F); - if (it == _mapping.end()) - return false; - auto it2 = _mapping.find(what); - if (it2 == _mapping.end()) - return false; - auto fn1 = _cg.get(it->second); - auto fn2 = _cg.get(it2->second); - if (fn1 && fn2) { - return fn1->calls(fn2); - } - return false; - }; - -}; - -/// -/// Callgraph that is built based on the results of pointer analysis -/// -class LLVMCallGraphImpl : public CallGraphImpl { - GenericCallGraph _cg{}; - const llvm::Module *_module; - LLVMPointerAnalysis *_pta; - - void processBBlock(const llvm::Function *parent, - const llvm::BasicBlock& B, - ADT::SetQueue>& queue) { - for (auto& I : B) { - if (auto *C = llvm::dyn_cast(&I)) { -#if LLVM_VERSION_MAJOR >= 8 - auto pts = _pta->getLLVMPointsTo(C->getCalledOperand()); -#else - auto pts = _pta->getLLVMPointsTo(C->getCalledValue()); -#endif - for (const auto& ptr : pts) { - auto *F = llvm::dyn_cast(ptr.value); - if (!F) - continue; - - _cg.addCall(parent, F); - queue.push(F); - } - } - } - } - - void build() { - auto *entry = _module->getFunction(_pta->getOptions().entryFunction); - assert(entry && "Entry function not found"); - _cg.createNode(entry); - - ADT::SetQueue> queue; - queue.push(entry); - - while (!queue.empty()) { - auto *cur = queue.pop(); - for (auto& B : *cur) { - processBBlock(cur, B, queue); - } - } - } - -public: - LLVMCallGraphImpl(const llvm::Module *m, LLVMPointerAnalysis *pta) - : _module(m), _pta(pta) { - build(); - } - - FuncVec functions() const override { - FuncVec ret; - for (auto& it : _cg) { - ret.push_back(it.first); - } - return ret; - } - - FuncVec callers(const llvm::Function *F) const override { - FuncVec ret; - auto fnd = _cg.get(F); - for (auto *nd : fnd->getCallers()) { - ret.push_back(nd->getValue()); - } - return ret; - } - - FuncVec callees(const llvm::Function *F) const override { - FuncVec ret; - auto fnd = _cg.get(F); - for (auto *nd : fnd->getCalls()) { - ret.push_back(nd->getValue()); - } - return ret; - } - - bool calls(const llvm::Function *F, const llvm::Function *what) const override { - auto fn1 = _cg.get(F); - auto fn2 = _cg.get(what); - if (fn1 && fn2) { - return fn1->calls(fn2); - } - return false; - }; - -}; - - -class CallGraph { - std::unique_ptr _impl; - -public: - using FuncVec = CallGraphImpl::FuncVec; - - CallGraph(GenericCallGraph& cg) : _impl(new DGCallGraphImpl(cg)) {} - CallGraph(const llvm::Module *m, LLVMPointerAnalysis *pta) - : _impl(new LLVMCallGraphImpl(m, pta)) {} - - /// - /// Get all functions in this call graph - /// - FuncVec functions() const { return _impl->functions(); } - /// - /// Get callers of a function - /// - FuncVec callers(const llvm::Function *F) const { return _impl->callers(F); }; - /// - /// Get functions called from the given function - /// - FuncVec callees(const llvm::Function *F) const { return _impl->callees(F); }; - /// - /// Return true if function 'F' calls 'what' - /// - bool calls(const llvm::Function *F, const llvm::Function *what) const { - return _impl->calls(F, what); - }; -}; - -} // llvmdg -} // dg - -#endif diff --git a/include/dg/llvm/ControlDependence/ControlClosure.h b/include/dg/llvm/ControlDependence/ControlClosure.h deleted file mode 100644 index 59a2f520a..000000000 --- a/include/dg/llvm/ControlDependence/ControlClosure.h +++ /dev/null @@ -1,141 +0,0 @@ -#ifndef DG_LLVM_CONTROL_CLOSURE_H_ -#define DG_LLVM_CONTROL_CLOSURE_H_ - -#include - -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "GraphBuilder.h" - -#include "dg/ControlDependence/ControlClosure.h" - -#include -#include -#include - - -namespace llvm { -class Function; -} - -namespace dg { -namespace llvmdg { - -class StrongControlClosure : public LLVMControlDependenceAnalysisImpl { - CDGraphBuilder graphBuilder{}; - - using CDResultT = std::map>; - - struct Info { - CDGraph graph; - - //// forward edges (from branchings to dependent blocks) - //CDResultT controlDependence{}; - //// reverse edges (from dependent blocks to branchings) - //CDResultT revControlDependence{}; - - Info(CDGraph&& graph) : graph(std::move(graph)) {} - }; - - std::unordered_map _graphs; - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - StrongControlClosure(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}) - : LLVMControlDependenceAnalysisImpl(module, opts) { - _graphs.reserve(module->size()); - } - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *) override { - assert(false && "Not supported"); - abort(); - } - - ValVec getDependent(const llvm::Instruction *) override { - assert(false && "Not supported"); - abort(); - } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - ValVec getClosure(const llvm::Function *F, const std::set& vals) override { - DBG(cda, "Computing closure of nodes in function " << F->getName().str()); - auto *graph = getGraph(F); - if (!graph) { - auto tmpgraph = graphBuilder.build(F, getOptions().nodePerInstruction()); - // FIXME: we can actually just forget the graph if we do not want to dump - // it to the user - auto it = _graphs.emplace(F, std::move(tmpgraph)); - graph = &it.first->second.graph; - } - - assert(graph); - - // TODO map values... - dg::StrongControlClosure sclosure; - std::set X; - for (auto *v : vals) { - X.insert(graphBuilder.getNode(v)); - } - auto cls = sclosure.getClosure(*graph, X); - std::vector retval; - for (auto *n : cls) { - retval.push_back(const_cast(graphBuilder.getValue(n))); - } - return retval; - } - - // We run on demand - void compute(const llvm::Function *F) override { - unsigned n = 0; - for (auto& B : *F) { - if (n == F->size() / 2) - getClosure(F, {const_cast(&B)}); - ++n; - } - /* we run on demand */ - } - - CDGraph *getGraph(const llvm::Function *f) override { return _getGraph(f); } - const CDGraph *getGraph(const llvm::Function *f) const override { return _getGraph(f); } - - // make this one public so that we can dump it in llvm-cda-dump - // (keep the _ prefix so that we can see that it should not be normally used...) - const Info *_getFunInfo(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - - Info *_getFunInfo(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - -private: - const CDGraph *_getGraph(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } - - CDGraph *_getGraph(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } -}; - -} // namespace llvmdg -} // namespace dg - -#endif - diff --git a/include/dg/llvm/ControlDependence/ControlDependence.h b/include/dg/llvm/ControlDependence/ControlDependence.h deleted file mode 100644 index a20eeabeb..000000000 --- a/include/dg/llvm/ControlDependence/ControlDependence.h +++ /dev/null @@ -1,122 +0,0 @@ -#ifndef LLVM_DG_CDA_H_ -#define LLVM_DG_CDA_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h" -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h" - -namespace dg { -//namespace cda { - -class LLVMPointerAnalysis; - -namespace llvmdg { - class CallGraph; -} - -class LLVMControlDependenceAnalysis { -public: - using ValVec = std::vector; - -private: - const llvm::Module *_module; - const LLVMControlDependenceAnalysisOptions _options; - std::unique_ptr _impl{nullptr}; - std::unique_ptr _interprocImpl{nullptr}; - - void initializeImpl(LLVMPointerAnalysis *pta = nullptr, - llvmdg::CallGraph *cg = nullptr); - - template - ValVec _getDependencies(ValT v) { - assert(_impl); - auto ret = _impl->getDependencies(v); - - if (getOptions().interproceduralCD()) { - assert(_interprocImpl); - auto interproc = _interprocImpl->getDependencies(v); - ret.insert(ret.end(), interproc.begin(), interproc.end()); - } - return ret; - } - - template - ValVec _getDependent(ValT v) { - assert(_impl); - auto ret = _impl->getDependent(v); - - if (getOptions().interproceduralCD()) { - assert(_interprocImpl); - auto interproc = _interprocImpl->getDependent(v); - ret.insert(ret.end(), interproc.begin(), interproc.end()); - } - return ret; - } - - -public: - LLVMControlDependenceAnalysis(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts) - : _module(module), _options(opts) { - initializeImpl(); - } - - // public API - const llvm::Module *getModule() const { return _module; } - const LLVMControlDependenceAnalysisOptions& getOptions() const { return _options; } - - LLVMControlDependenceAnalysisImpl *getImpl() { return _impl.get(); } - const LLVMControlDependenceAnalysisImpl *getImpl() const { return _impl.get(); } - - // Compute control dependencies for all functions. - // If the analysis works on demand, calling this method - // will trigger the computation for the given function - // or the whole module if the function is nullptr. - // (so you don't want to call that if you want - // on demand) - void compute(const llvm::Function *F = nullptr) { - _impl->compute(F); - if (getOptions().interproceduralCD()) - _interprocImpl->compute(F); - } - - ValVec getDependencies(const llvm::Instruction *v) { return _getDependencies(v); } - ValVec getDependent(const llvm::Instruction *v) { return _getDependent(v); } - - ValVec getDependencies(const llvm::BasicBlock *b) { return _getDependencies(b); } - ValVec getDependent(const llvm::BasicBlock *b) { return _getDependent(b); } - - ValVec getNoReturns(const llvm::Function *F) { - if (_interprocImpl) - return _interprocImpl->getNoReturns(F); - return {}; - } - - // A getter for results of closure-based algorithms. - // The method may abort if used with non-closure-based analysis. - ValVec getClosure(const llvm::Function *F, const std::set& vals) { - return _impl->getClosure(F, vals); - } - /// XXX TBD - //// A getter for iterative building of results of closure-based algorithms. - //void startClosure(const llvm::Function *F, const std::set& startSet) { - // return _impl->startClosure(F, startSet); - //} - //// A getter for iterative building of results of closure-based algorithms. - //void closeSet(const std::set& nodesset) { - // return _impl->closeSet(nodesset); - //} - - // FIXME: add also API that return just iterators -}; - -//} // namespace cda -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/DOD.h b/include/dg/llvm/ControlDependence/DOD.h deleted file mode 100644 index ad76f4067..000000000 --- a/include/dg/llvm/ControlDependence/DOD.h +++ /dev/null @@ -1,351 +0,0 @@ -#ifndef DG_LLVM_DOD_H_ -#define DG_LLVM_DOD_H_ - -#include - -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "GraphBuilder.h" -#include "IGraphBuilder.h" - -#include "dg/ControlDependence/DOD.h" -#include "dg/ControlDependence/DODNTSCD.h" - -#include -#include -#include - - -namespace llvm { -class Function; -} - -namespace dg { -namespace llvmdg { - -class DOD : public LLVMControlDependenceAnalysisImpl { - CDGraphBuilder graphBuilder{}; - - // although DOD is a ternary relation, we treat it as binary relation - // by forgetting the connection between dependant nodes. That is, - // for each p -> {a, b}, we have (p, a) and (p, b). - // This has no effect on slicing. If we will need that in the future, - // we can change this. - using CDResultT = std::map>; - - struct Info { - CDGraph graph; - - // forward edges (from branchings to dependent blocks) - CDResultT controlDependence{}; - // reverse edges (from dependent blocks to branchings) - CDResultT revControlDependence{}; - - Info(CDGraph&& graph) : graph(std::move(graph)) {} - }; - - std::unordered_map _graphs; - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - DOD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}) - : LLVMControlDependenceAnalysisImpl(module, opts) { - _graphs.reserve(module->size()); - } - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *I) override { - if (!getOptions().nodePerInstruction()) { - return {}; - } - - // XXX: this could be computed on-demand per one node (block) - // (in contrary to getDependent()) - auto *f = I->getParent()->getParent(); - if (_getGraph(f) == nullptr) { - /// FIXME: get rid of the const cast - computeOnDemand(const_cast(f)); - } - - assert(_getGraph(f) != nullptr); - - auto *node = graphBuilder.getNode(I); - if (!node) { - return {}; - } - auto *info = _getFunInfo(f); - assert(info && "Did not compute CD"); - - auto dit = info->controlDependence.find(node); - if (dit == info->controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = graphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - if (getOptions().nodePerInstruction()) { - return {}; - } - - if (_getGraph(b->getParent()) == nullptr) { - /// FIXME: get rid of the const cast - computeOnDemand(const_cast(b->getParent())); - } - assert(_getGraph(b->getParent()) != nullptr); - - auto *block = graphBuilder.getNode(b); - if (!block) { - return {}; - } - auto *info = _getFunInfo(b->getParent()); - assert(info && "Did not compute CD"); - - auto dit = info->controlDependence.find(block); - if (dit == info->controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = graphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - // We run on demand but this method can trigger the computation - void compute(const llvm::Function *F = nullptr) override { - DBG(cda, "Triggering computation of all dependencies"); - if (F && !F->isDeclaration() && (_getGraph(F) == nullptr)) { - computeOnDemand(const_cast(F)); - } else { - for (auto& f : *getModule()) { - if (!f.isDeclaration() && (_getGraph(&f) == nullptr)) { - computeOnDemand(const_cast(&f)); - } - } - } - } - - CDGraph *getGraph(const llvm::Function *f) override { return _getGraph(f); } - const CDGraph *getGraph(const llvm::Function *f) const override { return _getGraph(f); } - - // make this one public so that we can dump it in llvm-cda-dump - // (keep the _ prefix so that we can see that it should not be normally used...) - const Info *_getFunInfo(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - - Info *_getFunInfo(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - -private: - const CDGraph *_getGraph(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } - - CDGraph *_getGraph(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } - - void computeOnDemand(llvm::Function *F) { - DBG(cda, "Triggering on-demand computation for " << F->getName().str()); - assert(_getGraph(F) == nullptr - && "Already have the graph"); - - auto tmpgraph = graphBuilder.build(F, getOptions().nodePerInstruction()); - // FIXME: we can actually just forget the graph if we do not want to dump - // it to the user - auto it = _graphs.emplace(F, std::move(tmpgraph)); - - auto& info = it.first->second; - - if (getOptions().dodRanganathCD()) { - dg::DODRanganath dod; - auto result = dod.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } else if (getOptions().dodCD()) { - dg::DOD dod; - auto result = dod.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } else if (getOptions().dodntscdCD()) { - dg::DODNTSCD dodntscd; - auto result = dodntscd.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } else { - assert(false && "Wrong analysis type"); - abort(); - } - } -}; - - -// FIXME: this basically copies InterproceduralNTSCD, create -// a shared superclass... -class InterproceduralDOD : public LLVMControlDependenceAnalysisImpl { - ICDGraphBuilder igraphBuilder{}; - CDGraph graph; - - using CDResultT = std::map>; - // forward edges (from branchings to dependent blocks) - CDResultT controlDependence{}; - // reverse edges (from dependent blocks to branchings) - CDResultT revControlDependence{}; - bool _computed{false}; - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - /// - // Note: use this only when you know what you want. - // Computing intraprocedural CD + interprocedural CD - // separately is more efficient. - InterproceduralDOD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}, - LLVMPointerAnalysis *pta = nullptr, - CallGraph *cg = nullptr) - : LLVMControlDependenceAnalysisImpl(module, opts), igraphBuilder(pta, cg) {} - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *I) override { - if (!getOptions().nodePerInstruction()) { - return {}; - } - - _compute(); - - auto *node = igraphBuilder.getNode(I); - if (!node) { - return {}; - } - - assert(_computed && "CD is not computed"); - auto dit = controlDependence.find(node); - if (dit == controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = igraphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - if (getOptions().nodePerInstruction()) { - return {}; - } - - _compute(); - - auto *block = igraphBuilder.getNode(b); - if (!block) { - return {}; - } - - assert(_computed && "Did not compute CD"); - auto dit = controlDependence.find(block); - if (dit == controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = igraphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - // We run on demand but this method can trigger the computation - void compute(const llvm::Function *) override { - _compute(); - } - - /// Getter for noreturn nodes in function (for interprocedural analysis) - ValVec getNoReturns(const llvm::Function *) override { - assert(false && "Unsupported"); abort(); - } - - CDGraph *getGraph(const llvm::Function *) override { return &graph; } - const CDGraph *getGraph(const llvm::Function *) const override { return &graph; } - -private: - - void _compute() { - if (_computed) - return; - - DBG(cda, "Triggering computation of interprocedural NTSCD"); - - graph = igraphBuilder.build(getModule(), - getOptions().nodePerInstruction()); - - if (getOptions().dodRanganathCD()) { - dg::DODRanganath dod; - auto result = dod.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } else if (getOptions().dodCD()) { - dg::DOD dod; - auto result = dod.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } else if (getOptions().dodntscdCD()) { - dg::DODNTSCD dodntscd; - auto result = dodntscd.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } else { - assert(false && "Wrong analysis type"); - abort(); - } - - _computed = true; - } -}; - - -} // namespace llvmdg -} // namespace dg - - -#endif diff --git a/include/dg/llvm/ControlDependence/GraphBuilder.h b/include/dg/llvm/ControlDependence/GraphBuilder.h deleted file mode 100644 index f9ebc1912..000000000 --- a/include/dg/llvm/ControlDependence/GraphBuilder.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef DG_CDA_GRAPHBUILDER_H_ -#define DG_CDA_GRAPHBUILDER_H_ - -#include - -#include "llvm/IR/CFG.h" - -#include "dg/ControlDependence/CDGraph.h" -#include "dg/util/debug.h" - -namespace dg { -namespace llvmdg { - -class CDGraphBuilder { - using CDGraph = dg::CDGraph; - - // FIXME: store this per function (i.e., Function -> (Value -> CDNode)) - std::unordered_map _nodes; - std::unordered_map _rev_mapping; - - CDGraph buildInstructions(const llvm::Function *F) { - - DBG_SECTION_BEGIN(cda, "Building graph (of instructions) for " << F->getName().str()); - - CDGraph graph(F->getName().str()); - - struct BBlock { - std::vector nodes; - }; - - std::unordered_map _mapping; - _mapping.reserve(F->size()); - - // create nodes for blocks - for (auto& BB : *F) { - auto it = _mapping.emplace(&BB, BBlock()); - BBlock& block = it.first->second; - block.nodes.reserve(BB.size()); - for (auto& I : BB) { - auto& nd = graph.createNode(); - _rev_mapping[&nd] = &I; - _nodes[&I] = &nd; - block.nodes.push_back(&nd); - } - } - - // add successor edges - for (auto& BB : *F) { - auto& bblock = _mapping[&BB]; - CDNode *last = nullptr; - // successors inside the block - for (auto* nd : bblock.nodes) { - if (last) - graph.addNodeSuccessor(*last, *nd); - last = nd; - } - assert(last || BB.size() == 0); - - if (!last) - continue; - - // successors between blocks - for (auto *bbsucc : successors(&BB)) { - auto& succblk = _mapping[bbsucc]; - if (succblk.nodes.empty()) { - assert(bbsucc->size() == 0); - continue; - } - - graph.addNodeSuccessor(*last, *succblk.nodes.front()); - } - } - - DBG_SECTION_END(cda, "Done building graph for " << F->getName().str()); - - return graph; - } - - CDGraph buildBlocks(const llvm::Function *F) { - - DBG_SECTION_BEGIN(cda, "Building graph (of blocks) for " << F->getName().str()); - - CDGraph graph(F->getName().str()); - - std::unordered_map _mapping; - _mapping.reserve(F->size()); - _nodes.reserve(F->size() + _nodes.size()); - _rev_mapping.reserve(F->size() + _rev_mapping.size()); - - // create nodes for blocks - for (auto& BB : *F) { - auto& nd = graph.createNode(); - _mapping[&BB] = &nd; - _nodes[&BB] = &nd; - _rev_mapping[&nd] = &BB; - } - - // add successor edges - for (auto& BB : *F) { - auto *nd = _mapping[&BB]; - assert(nd && "BUG: creating nodes for bblocks"); - - for (auto *bbsucc : successors(&BB)) { - auto *succ = _mapping[bbsucc]; - assert(succ && "BUG: do not have a bblock created"); - graph.addNodeSuccessor(*nd, *succ); - } - } - - DBG_SECTION_END(cda, "Done building graph for function " << F->getName().str()); - - return graph; - } - -public: - - // \param instructions true if we should build nodes for the instructions - // instead of for basic blocks? - CDGraph build(const llvm::Function *F, bool instructions = false) { - if (instructions) { - return buildInstructions(F); - } - - return buildBlocks(F); - } - - CDNode *getNode(const llvm::Value *v) { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second; - } - - const CDNode *getNode(const llvm::Value *v) const { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second; - } - - const llvm::Value *getValue(const CDNode *n) const { - auto it = _rev_mapping.find(n); - return it == _rev_mapping.end() ? nullptr : it->second; - } - -}; - -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/IGraphBuilder.h b/include/dg/llvm/ControlDependence/IGraphBuilder.h deleted file mode 100644 index 3f08b53f7..000000000 --- a/include/dg/llvm/ControlDependence/IGraphBuilder.h +++ /dev/null @@ -1,296 +0,0 @@ -#ifndef DG_CDA_IGRAPHBUILDER_H_ -#define DG_CDA_IGRAPHBUILDER_H_ - -#include - -#include "llvm/IR/CFG.h" -#include "llvm/IR/Instructions.h" - -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/CallGraph/CallGraph.h" - -#include "dg/ControlDependence/CDGraph.h" -#include "GraphBuilder.h" -#include "dg/util/debug.h" - -namespace dg { -namespace llvmdg { - -namespace { - const llvm::Instruction * - getNextNonDebugInstruction(const llvm::Instruction *I) { -#if LLVM_VERSION_MAJOR >= 7 - return I->getNextNonDebugInstruction(); -#else - // this is the implementation of Instruction::getNextNonDebugInstruction() - // from LLVM 12 (adjusted) - for (const auto *NI = I->getNextNode(); NI; NI = NI->getNextNode()) - if (!llvm::isa(NI)) - return NI; - return nullptr; -#endif - } -} - -/// -// Whole-program graph for interprocedural analysis. -// Note that control dependencies include separate interprocedural analysis -// that fills in interprocedural CD into intraprocedural result. -// But this is another way how to do that.... (not used by default) -class ICDGraphBuilder { - using CDGraph = dg::CDGraph; - - struct CallInfo { - // called functions - std::vector funs; - - CallInfo(std::vector&& f) : funs(std::move(f)) {} - CallInfo(CallInfo&&) = default; - CallInfo(const CallInfo&) = delete; - }; - - std::unordered_map _nodes; - std::unordered_map _rev_mapping; - std::map calls; - - LLVMPointerAnalysis *_pta{nullptr}; - CallGraph *_cg{nullptr}; - - std::vector getCalledFunctions(const llvm::CallInst *C) { - auto *f = C->getCalledFunction(); - if (f) { - if (f->isDeclaration()) { - return {}; - } - return {f}; - } - - // function pointer call - if (_pta) { - abort(); - return {}; - } - if (_cg) { - abort(); - return {}; - } - - return {}; - } - - static const llvm::Value *_getEntryNode(const llvm::Function *f) { - assert(f && "Got no function"); - auto &B = f->getEntryBlock(); - return &*(B.begin()); - } - - CDGraph buildInstructions(const llvm::Module *M) { - DBG_SECTION_BEGIN(cda, "Building ICFG (of instructions) for the whole module"); - CDGraph graph("ICFG"); - - for (auto& F : *M) { - buildInstructions(graph, F); - } - - // add interprocedural edges - for (auto &it : calls) { - auto *C = it.first; - // call edge - for (auto *f : it.second.funs) { - auto *cnode = getNode(C); - assert(cnode && "Do not have the node for a call"); - auto *entrynode = getNode(_getEntryNode(f)); - assert(entrynode); - graph.addNodeSuccessor(*cnode, *entrynode); - - // return edges - auto *retsite = getNextNonDebugInstruction(C); - assert(retsite); - auto *retsitenode = getNode(retsite); - assert(retsitenode); - for (auto& B : *f) { - if (auto *R = llvm::dyn_cast(B.getTerminator())) { - auto *rnode = getNode(R); - graph.addNodeSuccessor(*rnode, *retsitenode); - } - } - } - } - - DBG_SECTION_END(cda, "Done building interprocedural CD graph"); - - return graph; - } - - void buildInstructions(CDGraph& graph, const llvm::Function& F) { - struct BBlock { - std::vector nodes; - }; - - std::unordered_map _mapping; - //_mapping.reserve(F->size()); - - // create nodes for instructions - for (auto& BB : F) { - auto it = _mapping.emplace(&BB, BBlock()); - BBlock& block = it.first->second; - block.nodes.reserve(BB.size()); - for (auto& I : BB) { - if (auto *C = llvm::dyn_cast(&I)) { - auto funs = getCalledFunctions(C); - if (!funs.empty()) - calls.emplace(C, CallInfo(std::move(funs))); - } - - auto& nd = graph.createNode(); - _rev_mapping[&nd] = &I; - _nodes[&I] = &nd; - block.nodes.push_back(&nd); - } - } - - // add intraprocedural successor edges - for (auto& BB : F) { - auto& bblock = _mapping[&BB]; - CDNode *last = nullptr; - // successors inside the block - for (auto* nd : bblock.nodes) { - if (last) - graph.addNodeSuccessor(*last, *nd); - auto *C = llvm::dyn_cast(getValue(nd)); - if (C && (calls.find(C) != calls.end())) { - // if the node is a call that calls some defined functions - // (we store only such in calls), its successor - // is going to be the entry to the procedure - last = nullptr; - } else { - last = nd; - } - } - // every block has at least one (terminator) instruction - assert(last && "Empty block"); - - // successors between blocks - for (auto *bbsucc : successors(&BB)) { - auto& succblk = _mapping[bbsucc]; - if (succblk.nodes.empty()) { - assert(bbsucc->size() == 0); - continue; - } - - graph.addNodeSuccessor(*last, *succblk.nodes.front()); - } - } - } - - CDGraph buildBlocks(const llvm::Module *M) { - DBG_SECTION_BEGIN(cda, "Building ICFG (of blocks) for the whole module"); - CDGraph graph("ICFG"); - - for (auto& F : *M) { - buildBlocks(graph, F); - } - - // add successor edges - for (auto &F : *M) { - for (auto& BB : F) { - auto *nd = getNode(&BB); - assert(nd && "BUG: creating nodes for bblocks"); - auto *blknd = nd; - - // add interprocedural edges - for (auto& I : BB) { - auto *C = llvm::dyn_cast(&I); - if (!C) { - continue; - } - - // create a block that represents the rest of the block - // and add an edge from the returns of f - const auto& funs = getCalledFunctions(C); - if (funs.empty()) - continue; - - auto& retsite = graph.createNode(); - - // call inst - for (auto *f : getCalledFunctions(C)) { - auto *entrynode = getNode(&f->getEntryBlock()); - assert(entrynode); - graph.addNodeSuccessor(*blknd, *entrynode); - - // return edges - for (auto& B : *f) { - if (llvm::isa(B.getTerminator())) { - // the block returns - auto *rnode = getNode(&B); - assert(rnode); - graph.addNodeSuccessor(*rnode, retsite); - } - } - } - blknd = &retsite; - } - - assert(blknd); - // add intraprocedural edges - for (auto *bbsucc : successors(&BB)) { - auto *succ = getNode(bbsucc); - assert(succ && "BUG: do not have a bblock created"); - graph.addNodeSuccessor(*blknd, *succ); - } - } - } - - return graph; - } - - void buildBlocks(CDGraph& graph, const llvm::Function& F) { - DBG_SECTION_BEGIN(cda, "Building ICFG (of blocks) for " << F.getName().str()); - - // create nodes for blocks - for (auto& BB : F) { - auto& nd = graph.createNode(); - _nodes[&BB] = &nd; - _rev_mapping[&nd] = &BB; - } - - DBG_SECTION_END(cda, "Done building graph for function " << F.getName().str()); - } - -public: - - ICDGraphBuilder(LLVMPointerAnalysis *pta = nullptr, CallGraph *cg = nullptr): - _pta(pta), _cg(cg) {} - - // \param instructions true if we should build nodes for the instructions - // instead of for basic blocks? - CDGraph build(const llvm::Module *M, bool instructions = false) { - if (instructions) { - return buildInstructions(M); - } - - return buildBlocks(M); - } - - CDNode *getNode(const llvm::Value *v) { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second; - } - - const CDNode *getNode(const llvm::Value *v) const { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second; - } - - const llvm::Value *getValue(const CDNode *n) const { - auto it = _rev_mapping.find(n); - return it == _rev_mapping.end() ? nullptr : it->second; - } - -}; - -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/InterproceduralCD.h b/include/dg/llvm/ControlDependence/InterproceduralCD.h deleted file mode 100644 index e2adfd702..000000000 --- a/include/dg/llvm/ControlDependence/InterproceduralCD.h +++ /dev/null @@ -1,143 +0,0 @@ -#ifndef DG_LLVM_INTERPROC_CD_H_ -#define DG_LLVM_INTERPROC_CD_H_ - -#include - -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h" - -#include -#include -#include - - -namespace llvm { -class Function; -} - -namespace dg { - -class LLVMPointerAnalysis; - -namespace llvmdg { - -class CallGraph; - - -class LLVMInterprocCD : public LLVMControlDependenceAnalysisImpl { - LLVMPointerAnalysis *PTA{nullptr}; - // CallGraph *_cg{nullptr}; - - struct FuncInfo { - // points that may abort the program - // (or cause infinite looping). That is, - // points due to which the function may not return - // to its caller - std::set noret; - bool hasCD = false; - }; - - std::unordered_map> _instrCD; - std::unordered_map> _blockCD; - std::unordered_map _funcInfos; - - FuncInfo *getFuncInfo(const llvm::Function *F) { - auto it = _funcInfos.find(F); - return it == _funcInfos.end() ? nullptr : &it->second; - } - - const FuncInfo *getFuncInfo(const llvm::Function *F) const { - auto it = _funcInfos.find(F); - return it == _funcInfos.end() ? nullptr : &it->second; - } - - - bool hasFuncInfo(const llvm::Function *fun) const { - return _funcInfos.find(fun) != _funcInfos.end(); - } - - // recursively compute function info, 'stack' is there to detect recursive calls - void computeFuncInfo(const llvm::Function *fun, - std::set stack = {}); - void computeCD(const llvm::Function *fun); - - std::vector getCalledFunctions(const llvm::Value *v); - -public: - using ValVec = LLVMControlDependenceAnalysisImpl::ValVec; - - LLVMInterprocCD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}, - LLVMPointerAnalysis *pta = nullptr, - CallGraph * /* cg */ = nullptr) - : LLVMControlDependenceAnalysisImpl(module, opts), PTA(pta) - /*, _cg(cg) */ {} - - ValVec getNoReturns(const llvm::Function *fun) override { - ValVec ret; - auto *fi = getFuncInfo(fun); - if (!fi) { - computeFuncInfo(fun); - } - fi = getFuncInfo(fun); - assert(fi && "BUG in computeFuncInfo"); - - for (auto *val : fi->noret) - ret.push_back(const_cast(val)); - return ret; - } - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *I) override { - auto *fun = I->getParent()->getParent(); - auto *fi = getFuncInfo(fun); - if (!fi) { - computeFuncInfo(fun); - } - - fi = getFuncInfo(fun); - assert(fi && "BUG in computeFuncInfo"); - if (!fi->hasCD) { - computeCD(fun); - assert(fi->hasCD && "BUG in computeCD"); - } - - ValVec ret; - auto instrIt = _instrCD.find(I); - if (instrIt != _instrCD.end()) { - ret.insert(ret.end(), instrIt->second.begin(), instrIt->second.end()); - } - - auto blkIt = _blockCD.find(I->getParent()); - if (blkIt != _blockCD.end()) { - ret.insert(ret.end(), blkIt->second.begin(), blkIt->second.end()); - } - - return ret; - } - - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *) override { return {}; } - ValVec getDependent(const llvm::BasicBlock *) override { return {}; } - - void compute(const llvm::Function *F = nullptr) override { - if (F && !F->isDeclaration()) { - if (!hasFuncInfo(F)) { - computeFuncInfo(F); - } - } else { - for (auto& f : *getModule()) { - if (!f.isDeclaration() && !hasFuncInfo(&f)) { - computeFuncInfo(&f); - } - } - } - - } -}; - -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h b/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h deleted file mode 100644 index 30dd1d9b5..000000000 --- a/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisImpl.h +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef LLVM_DG_CDA_IMPL_H_ -#define LLVM_DG_CDA_IMPL_H_ - - -#include - -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h" - -namespace llvm { - class Module; - class Value; - class Function; -}; - -namespace dg { - -class CDGraph; - -class LLVMControlDependenceAnalysisImpl { - - const llvm::Module *_module; - const LLVMControlDependenceAnalysisOptions _options; - -public: - LLVMControlDependenceAnalysisImpl(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts) - : _module(module), _options(opts) {} - - virtual ~LLVMControlDependenceAnalysisImpl() = default; - - using ValVec = std::vector; - - // public API - const llvm::Module *getModule() const { return _module; } - const LLVMControlDependenceAnalysisOptions& getOptions() const { return _options; } - - virtual CDGraph *getGraph(const llvm::Function *) { return nullptr; } - virtual const CDGraph *getGraph(const llvm::Function *) const { return nullptr; } - - // Compute control dependencies for all functions. - // If the analysis works on demand, calling this method - // will trigger the computation for the given function - // or the whole module if the function is nullptr. - // (so you don't want to call that if you want - // on demand) - virtual void compute(const llvm::Function *F = nullptr) = 0; - - /// Getters of dependencies for a value - virtual ValVec getDependencies(const llvm::Instruction *) = 0; - virtual ValVec getDependent(const llvm::Instruction *) = 0; - - /// Getters of dependencies for a basic block - virtual ValVec getDependencies(const llvm::BasicBlock *) = 0; - virtual ValVec getDependent(const llvm::BasicBlock *) = 0; - - /// Getter for noreturn nodes in function (for interprocedural analysis) - virtual ValVec getNoReturns(const llvm::Function *) { - assert(false && "Unsupported"); abort(); - } - - virtual ValVec getClosure(const llvm::Function *, const std::set&) { - assert(false && "Unsupported"); abort(); - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h b/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h deleted file mode 100644 index b04eff702..000000000 --- a/include/dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DG_LLVM_CDA_OPTIONS_H_ -#define DG_LLVM_CDA_OPTIONS_H_ - -#include "dg/llvm/LLVMAnalysisOptions.h" -#include "dg/ControlDependence/ControlDependenceAnalysisOptions.h" - -namespace dg { - -struct LLVMControlDependenceAnalysisOptions : - public LLVMAnalysisOptions, ControlDependenceAnalysisOptions -{ - bool _nodePerInstruction{false}; - bool _icfg{false}; - - void setNodePerInstruction(bool b) { _nodePerInstruction = b; } - bool nodePerInstruction() const { return _nodePerInstruction; } - bool ICFG() const { return _icfg; } -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/NTSCD.h b/include/dg/llvm/ControlDependence/NTSCD.h deleted file mode 100644 index 50b5e5051..000000000 --- a/include/dg/llvm/ControlDependence/NTSCD.h +++ /dev/null @@ -1,349 +0,0 @@ -#ifndef DG_LLVM_NTSCD_H_ -#define DG_LLVM_NTSCD_H_ - -#include - -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "GraphBuilder.h" -#include "IGraphBuilder.h" - -#include "dg/ControlDependence/NTSCD.h" - -#include -#include -#include - - -namespace llvm { -class Function; -} - -namespace dg { -namespace llvmdg { - -class NTSCD : public LLVMControlDependenceAnalysisImpl { - CDGraphBuilder graphBuilder{}; - - using CDResultT = std::map>; - - struct Info { - CDGraph graph; - - // forward edges (from branchings to dependent blocks) - CDResultT controlDependence{}; - // reverse edges (from dependent blocks to branchings) - CDResultT revControlDependence{}; - - Info(CDGraph&& graph) : graph(std::move(graph)) {} - }; - - std::unordered_map _graphs; - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - NTSCD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}) - : LLVMControlDependenceAnalysisImpl(module, opts) { - _graphs.reserve(module->size()); - } - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *I) override { - if (!getOptions().nodePerInstruction()) { - return {}; - } - - // XXX: this could be computed on-demand per one node (block) - // (in contrary to getDependent()) - auto *f = I->getParent()->getParent(); - if (_getGraph(f) == nullptr) { - /// FIXME: get rid of the const cast - computeOnDemand(const_cast(f)); - } - - assert(_getGraph(f) != nullptr); - - auto *node = graphBuilder.getNode(I); - if (!node) { - return {}; - } - auto *info = _getFunInfo(f); - assert(info && "Did not compute CD"); - - auto dit = info->controlDependence.find(node); - if (dit == info->controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = graphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - if (getOptions().nodePerInstruction()) { - return {}; - } - - // XXX: this could be computed on-demand per one node (block) - // (in contrary to getDependent()) - if (_getGraph(b->getParent()) == nullptr) { - /// FIXME: get rid of the const cast - computeOnDemand(const_cast(b->getParent())); - } - assert(_getGraph(b->getParent()) != nullptr); - - auto *block = graphBuilder.getNode(b); - if (!block) { - return {}; - } - auto *info = _getFunInfo(b->getParent()); - assert(info && "Did not compute CD"); - - auto dit = info->controlDependence.find(block); - if (dit == info->controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = graphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - // We run on demand but this method can trigger the computation - void compute(const llvm::Function *F = nullptr) override { - DBG(cda, "Triggering computation of all dependencies"); - if (F && !F->isDeclaration() && (_getGraph(F) == nullptr)) { - computeOnDemand(const_cast(F)); - } else { - for (auto& f : *getModule()) { - if (!f.isDeclaration() && (_getGraph(&f) == nullptr)) { - computeOnDemand(const_cast(&f)); - } - } - } - } - - CDGraph *getGraph(const llvm::Function *f) override { return _getGraph(f); } - const CDGraph *getGraph(const llvm::Function *f) const override { return _getGraph(f); } - - // make this one public so that we can dump it in llvm-cda-dump - // (keep the _ prefix so that we can see that it should not be normally used...) - const Info *_getFunInfo(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - - Info *_getFunInfo(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second; - } - -private: - const CDGraph *_getGraph(const llvm::Function *f) const { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } - - CDGraph *_getGraph(const llvm::Function *f) { - auto it = _graphs.find(f); - return it == _graphs.end() ? nullptr : &it->second.graph; - } - - void computeOnDemand(llvm::Function *F) { - DBG(cda, "Triggering on-demand computation for " << F->getName().str()); - assert(_getGraph(F) == nullptr - && "Already have the graph"); - - auto tmpgraph = graphBuilder.build(F, getOptions().nodePerInstruction()); - // FIXME: we can actually just forget the graph if we do not want to dump - // it to the user - auto it = _graphs.emplace(F, std::move(tmpgraph)); - - auto& info = it.first->second; - - const auto& opts = getOptions(); - if (opts.ntscd2CD()) { - DBG(cda, "Using the NTSCD 2 algorithm"); - dg::NTSCD2 ntscd; - auto result = ntscd.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } else if (opts.ntscdRanganathCD() || opts.ntscdRanganathOrigCD()) { - DBG(cda, "Using the NTSCD Ranganath algorithm"); - dg::NTSCDRanganath ntscd; - if (opts.ntscdRanganathOrigCD()) { - auto result = ntscd.compute(info.graph, /* doFixpoint= */ false); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } else { - auto result = ntscd.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } - } else { - assert(opts.ntscdCD() && "Wrong analysis type"); - dg::NTSCD ntscd; - auto result = ntscd.compute(info.graph); - info.controlDependence = std::move(result.first); - info.revControlDependence = std::move(result.second); - } - } -}; - -class InterproceduralNTSCD : public LLVMControlDependenceAnalysisImpl { - ICDGraphBuilder igraphBuilder{}; - CDGraph graph; - - using CDResultT = std::map>; - // forward edges (from branchings to dependent blocks) - CDResultT controlDependence{}; - // reverse edges (from dependent blocks to branchings) - CDResultT revControlDependence{}; - bool _computed{false}; - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - /// - // Note: use this only when you know what you want. - // Computing intraprocedural CD + interprocedural CD - // separately is more efficient. - InterproceduralNTSCD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}, - LLVMPointerAnalysis *pta = nullptr, - CallGraph *cg = nullptr) - : LLVMControlDependenceAnalysisImpl(module, opts), igraphBuilder(pta, cg) {} - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *I) override { - if (!getOptions().nodePerInstruction()) { - return {}; - } - - _compute(); - - auto *node = igraphBuilder.getNode(I); - if (!node) { - return {}; - } - - assert(_computed && "CD is not computed"); - auto dit = controlDependence.find(node); - if (dit == controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = igraphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - if (getOptions().nodePerInstruction()) { - return {}; - } - - _compute(); - - auto *block = igraphBuilder.getNode(b); - if (!block) { - return {}; - } - - assert(_computed && "Did not compute CD"); - auto dit = controlDependence.find(block); - if (dit == controlDependence.end()) - return {}; - - std::set ret; - for (auto *dep : dit->second) { - auto *val = igraphBuilder.getValue(dep); - assert(val && "Invalid value"); - ret.insert(const_cast(val)); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - // We run on demand but this method can trigger the computation - void compute(const llvm::Function *) override { - _compute(); - } - - /// Getter for noreturn nodes in function (for interprocedural analysis) - ValVec getNoReturns(const llvm::Function *) override { - assert(false && "Unsupported"); abort(); - } - - CDGraph *getGraph(const llvm::Function *) override { return &graph; } - const CDGraph *getGraph(const llvm::Function *) const override { return &graph; } - -private: - - void _compute() { - if (_computed) - return; - - DBG(cda, "Triggering computation of interprocedural NTSCD"); - - graph = igraphBuilder.build(getModule(), - getOptions().nodePerInstruction()); - - if (getOptions().ntscd2CD()) { - DBG(cda, "Using the NTSCD 2 algorithm"); - dg::NTSCD2 ntscd; - auto result = ntscd.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } else if (getOptions().ntscdRanganathCD()) { - DBG(cda, "Using the NTSCD Ranganath algorithm"); - dg::NTSCDRanganath ntscd; - auto result = ntscd.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } else { - assert(getOptions().ntscdCD() && "Wrong analysis type"); - dg::NTSCD ntscd; - auto result = ntscd.compute(graph); - controlDependence = std::move(result.first); - revControlDependence = std::move(result.second); - } - - _computed = true; - } -}; - -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/SCD.h b/include/dg/llvm/ControlDependence/SCD.h deleted file mode 100644 index 475ab0a65..000000000 --- a/include/dg/llvm/ControlDependence/SCD.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef DG_LLVM_SCD_H_ -#define DG_LLVM_SCD_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "dg/util/debug.h" - -#include -#include -#include - - -namespace llvm { -class Function; -} - -namespace dg { - -class LLVMPointerAnalysis; - -namespace llvmdg { - -// Standard control dependencies based on the computation -// of post-dominance frontiers. -// This class uses purely LLVM, no internal representation -// like the other classes (we use the post-dominance computation from LLVM). -class SCD : public LLVMControlDependenceAnalysisImpl { - - void computePostDominators(llvm::Function& F); - - std::unordered_map> dependentBlocks; - std::unordered_map> dependencies; - std::set _computed; - - void computeOnDemand(const llvm::Function *F) { - if (_computed.insert(F).second) { - computePostDominators(*const_cast(F)); - } - } - -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - SCD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}) - : LLVMControlDependenceAnalysisImpl(module, opts) {} - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *) override { return {}; } - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - computeOnDemand(b->getParent()); - - auto &S = dependencies[b]; - return ValVec{S.begin(), S.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *b) override { - computeOnDemand(b->getParent()); - - auto &S = dependentBlocks[b]; - return ValVec{S.begin(), S.end()}; - } - - void compute(const llvm::Function *F = nullptr) override { - DBG(cda, "Triggering computation of all dependencies"); - if (F && !F->isDeclaration()) { - computeOnDemand(F); - } else { - for (auto& f : *getModule()) { - if (f.isDeclaration()) { - continue; - } - computeOnDemand(&f); - } - } - } -}; - -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/legacy/Block.h b/include/dg/llvm/ControlDependence/legacy/Block.h deleted file mode 100644 index 457f471c3..000000000 --- a/include/dg/llvm/ControlDependence/legacy/Block.h +++ /dev/null @@ -1,97 +0,0 @@ -#ifndef DG_LEGACY_NTSCD_BLOCK_H -#define DG_LEGACY_NTSCD_BLOCK_H - -#include -#include -#include -#include - -namespace llvm { - class Instruction; - class Function; - class BasicBlock; -} - -namespace dg { -namespace llvmdg { -namespace legacy { - -class Function; - -class Block { - const llvm::BasicBlock *_llvm_blk; -public: - - Block(const llvm::BasicBlock *b, bool callReturn = false) - : _llvm_blk(b), callReturn(callReturn) {} - - // FIXME: make vector - const std::set & predecessors() const; - const std::set & successors() const; - - bool addPredecessor(Block * predecessor); - bool removePredecessor(Block * predecessor); - - bool addSuccessor(Block * successor); - bool removeSuccessor(Block * successor); - - const std::vector & llvmInstructions() const { return llvmInstructions_; } - - const llvm::Instruction * lastInstruction() const; - - bool addInstruction(const llvm::Instruction * instruction); - - bool addCallee(const llvm::Function * llvmFunction, Function * function); - bool addFork(const llvm::Function * llvmFunction, Function * function); - bool addJoin(const llvm::Function * llvmFunction, Function * function); - - const std::map & callees() const; - std::map callees(); - - const std::map & forks() const; - std::map forks(); - - const std::map & joins() const; - std::map joins(); - - bool isCall() const; - bool isArtificial() const; - bool isCallReturn() const; - bool isExit() const; - - void traversalId() { traversalId_ = ++traversalCounter; } - int bfsId() const { return traversalId_; } - - const llvm::BasicBlock * llvmBlock() const { return _llvm_blk; } - - std::string dotName() const; - - std::string label() const; - - void visit(); - - void dumpNode(std::ostream & ostream) const; - void dumpEdges(std::ostream & ostream) const; - - -private: - static int traversalCounter; - - std::vector llvmInstructions_; - - std::set predecessors_; - std::set successors_; - - bool callReturn = false; - int traversalId_ = 0; - - std::map callees_; - std::map forks_; - std::map joins_; -}; - -} -} -} - -#endif // DG_LLVM_BLOCK_H diff --git a/include/dg/llvm/ControlDependence/legacy/Function.h b/include/dg/llvm/ControlDependence/legacy/Function.h deleted file mode 100644 index 6dc3190f0..000000000 --- a/include/dg/llvm/ControlDependence/legacy/Function.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef DG_LEGACY_NTSCD_FUNCTION_H -#define DG_LEGACY_NTSCD_FUNCTION_H - -#include -#include - -namespace dg { -namespace llvmdg { -namespace legacy { - -class Block; - -class Function { -public: - - Function(); - ~Function(); - - Block *entry() const; - Block *exit() const; - - bool addBlock(Block *block); - - std::set nodes() const; - std::set condNodes() const; - std::set callReturnNodes() const; - - void dumpBlocks(std::ostream& ostream); - void dumpEdges(std::ostream& ostream); - -private: - - Block *firstBlock = nullptr; - Block *lastBlock = nullptr; - std::set blocks; -}; - -} -} -} - -#endif diff --git a/include/dg/llvm/ControlDependence/legacy/GraphBuilder.h b/include/dg/llvm/ControlDependence/legacy/GraphBuilder.h deleted file mode 100644 index 23024a5b8..000000000 --- a/include/dg/llvm/ControlDependence/legacy/GraphBuilder.h +++ /dev/null @@ -1,79 +0,0 @@ -#ifndef DG_LEGACY_NTSCD_GRAPHBUILDER_H -#define DG_LEGACY_NTSCD_GRAPHBUILDER_H - -#include -#include -#include -#include - -namespace llvm { -class Value; -class Function; -class BasicBlock; -class Instruction; -class CallInst; -} - -namespace dg { - -class LLVMPointerAnalysis; - -namespace llvmdg { -namespace legacy { - -class Function; -class Block; - -class GraphBuilder { - - LLVMPointerAnalysis *pointsToAnalysis_ = nullptr; - bool threads = false; - std::map _functions; - // mapping from llvm block to our blocks (a vector of blocks - // since we split them apart) - // FIXME: optimize this, most of the vectors will be just a singleton... - std::unordered_map> _mapping; - - std::vector getCalledFunctions(const llvm::Value *v); - - void handleCallInstruction(const llvm::Instruction *instruction, - Block *lastBlock, - bool& createBlock, - bool& createCallReturn); - - bool createPthreadCreate(const llvm::CallInst * callInst, Block * lastBlock); - bool createPthreadJoin(const llvm::CallInst * callInst, Block * lastBlock); - -public: - - GraphBuilder(LLVMPointerAnalysis *pointsToAnalysis = nullptr); - ~GraphBuilder(); - - Function *buildFunction(const llvm::Function * llvmFunction, bool recursively = false); - Function *findFunction(const llvm::Function * llvmFunction); - Function *createOrGetFunction(const llvm::Function * llvmFunction); - - const std::map& functions() const { - return _functions; - } - - const std::vector *mapBlock(const llvm::BasicBlock *b) const { - auto it = _mapping.find(b); - return it == _mapping.end() ? nullptr : &it->second; - } - - void dumpNodes(std::ostream& ostream) const; - void dumpEdges(std::ostream& ostream) const; - void dump(std::ostream& ostream) const; -}; - -// auxiliary functions -int predecessorsNumber(const llvm::BasicBlock *basicBlock); -int successorsNumber(const llvm::BasicBlock *basicBlock); -bool isReachable(const llvm::BasicBlock *basicBlock); - -} -} -} - -#endif diff --git a/include/dg/llvm/ControlDependence/legacy/NTSCD.h b/include/dg/llvm/ControlDependence/legacy/NTSCD.h deleted file mode 100644 index 4c4009a94..000000000 --- a/include/dg/llvm/ControlDependence/legacy/NTSCD.h +++ /dev/null @@ -1,132 +0,0 @@ -#ifndef DG_LEGACY_LLVM_NTSCD_H_ -#define DG_LEGACY_LLVM_NTSCD_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "GraphBuilder.h" -#include "dg/util/debug.h" - -#include -#include -#include - -#include "Block.h" - - -namespace llvm { -class Function; -} - -namespace dg { - -class LLVMPointerAnalysis; - -namespace llvmdg { -namespace legacy { - -class NTSCD : public LLVMControlDependenceAnalysisImpl { -public: - using ValVec = LLVMControlDependenceAnalysis::ValVec; - - struct NodeInfo { - bool visited = false; - bool red = false; - size_t outDegreeCounter = 0; - }; - - NTSCD(const llvm::Module *module, - const LLVMControlDependenceAnalysisOptions& opts = {}, - LLVMPointerAnalysis *pointsToAnalysis = nullptr); - - void dump(std::ostream & ostream) const; - void dumpDependencies(std::ostream & ostream) const; - - const std::map>& - controlDependencies() const { return controlDependency; } - - /// Getters of dependencies for a value - ValVec getDependencies(const llvm::Instruction *) override { return {}; } - ValVec getDependent(const llvm::Instruction *) override { return {}; } - - /// Getters of dependencies for a basic block - ValVec getDependencies(const llvm::BasicBlock *b) override { - if (_computed.insert(b->getParent()).second) { - /// FIXME: get rid of the const cast - computeOnDemand(const_cast(b->getParent())); - } - - auto *block = graphBuilder.mapBlock(b); - if (!block) { - return {}; - } - auto it = revControlDependency.find(block->front()); - if (it == revControlDependency.end()) - return {}; - - std::set ret; - for (auto *dep : it->second) { - assert(dep->llvmBlock() && "Do not have LLVM block"); - ret.insert(const_cast(dep->llvmBlock())); - } - - return ValVec{ret.begin(), ret.end()}; - } - - ValVec getDependent(const llvm::BasicBlock *) override { - assert(false && "Not supported"); - abort(); - } - - - // We run on demand. However, you may use manually computeDependencies() - // to compute all dependencies in the interprocedural CFG. - void compute(const llvm::Function *F = nullptr) override { - DBG(cda, "Triggering computation of all dependencies"); - if (F && !F->isDeclaration() && _computed.insert(F).second) { - computeOnDemand(const_cast(F)); - } else { - for (auto& f : *getModule()) { - if (!f.isDeclaration() && _computed.insert(&f).second) { - computeOnDemand(const_cast(&f)); - } - } - } - } - - // Compute dependencies for the whole ICFG (used in legacy code) - void computeDependencies(); - -private: - GraphBuilder graphBuilder; - - // forward edges (from branchings to dependent blocks) - std::map> controlDependency; - // reverse edges (from dependent blocks to branchings) - std::map> revControlDependency; - std::unordered_map nodeInfo; - std::set _computed; // for on-demand - - void computeDependencies(Function *); - void computeOnDemand(llvm::Function *F); - - void computeInterprocDependencies(Function *function); - void computeIntraprocDependencies(Function *function); - - // a is CD on b - void addControlDependence(Block *a, Block *b); - - void visitInitialNode(Block * node); - void visit(Block * node); - - bool hasRedAndNonRedSuccessor(Block * node); -}; - -} // namespace legacy -} // namespace llvmdg -} // namespace dg - -#endif diff --git a/include/dg/llvm/ControlDependence/legacy/TarjanAnalysis.h b/include/dg/llvm/ControlDependence/legacy/TarjanAnalysis.h deleted file mode 100644 index fb875e2e7..000000000 --- a/include/dg/llvm/ControlDependence/legacy/TarjanAnalysis.h +++ /dev/null @@ -1,175 +0,0 @@ -#ifndef DG_LEGACY_NTSCD_TARJANANALYSIS_H -#define DG_LEGACY_NTSCD_TARJANANALYSIS_H - -#include -#include -#include -#include -#include -#include -#include - -namespace dg { -namespace llvmdg { -namespace legacy { - -// FIXME: we've got this and SCC, unify it -template -class TarjanAnalysis -{ -public: - class StronglyConnectedComponent - { - private: - static int idCounter; - public: - StronglyConnectedComponent():id_(++idCounter) {} - - void addNode(T * node) { nodes_.push_back(node); } - - bool addSuccessor(StronglyConnectedComponent * successor) { - if (!successor) { - return false; - } - successors_.insert(successor); - return successor->predecessors_.insert(this).second; - } - - bool addPredecessor(StronglyConnectedComponent * predecessor) { - if (!predecessor) { - return false; - } - predecessors_.insert(predecessor); - return predecessor->successors_.insert(this).second; - } - - int id() const {return id_; } - - const std::vector & nodes() const {return nodes_; } - - const std::set & predecessors() const { return predecessors_; } - - const std::set & successors() const { return successors_; } - private: - int id_; - std::vector nodes_; - std::set successors_; - std::set predecessors_; - }; - - // this we have to remember for each node - struct Node { - int dfsId{0}; - int lowLink{0}; - bool onStack{false}; - StronglyConnectedComponent * component{nullptr}; - }; - - TarjanAnalysis(std::size_t size = 0):nodeInfo(size) {} - - ~TarjanAnalysis() { - for (auto component : components_) { - delete component; - } - } - - void compute(T * currentNode) { - ++index; - nodeInfo[currentNode].dfsId = index; - nodeInfo[currentNode].lowLink = index; - - stack.push(currentNode); - nodeInfo[currentNode].onStack = true; - - for (auto successor : currentNode->successors()) { - if (!visited(successor)) { - compute(successor); - nodeInfo[currentNode].lowLink = std::min(nodeInfo[currentNode].lowLink, - nodeInfo[successor].lowLink); - } else if (nodeInfo[successor].onStack) { - nodeInfo[currentNode].lowLink = std::min(nodeInfo[currentNode].lowLink, - nodeInfo[successor].dfsId); - } - } - - if (nodeInfo[currentNode].lowLink == nodeInfo[currentNode].dfsId) { - auto component = new StronglyConnectedComponent(); - components_.insert(component); - - T * node; - while (nodeInfo[stack.top()].dfsId >= nodeInfo[currentNode].dfsId) { - node = stack.top(); - stack.pop(); - nodeInfo[node].onStack = false; - component->addNode(node); - nodeInfo[node].component = component; - - if (stack.empty()) { - break; - } - } - } - } - - void computeCondensation() { - for (auto component : components_) { - for (auto node : component->nodes()) { - for (auto successor : node->successors()) { - if (nodeInfo[node].component != nodeInfo[successor].component) { - nodeInfo[node].component->addSuccessor(nodeInfo[successor].component); - } - } - } - } - } - - std::set computeBackWardReachability(T * node) { - std::set visitedComponents; - std::queue queue; - - auto iterator = nodeInfo.find(node); - if (iterator == nodeInfo.end()) { - return visitedComponents; - } - auto initialComponent = iterator.second; - visitedComponents.insert(initialComponent); - queue.push(initialComponent); - - while (!queue.empty()) { - auto component = queue.front(); - queue.pop(); - for (auto predecessor : component->predecessors()) { - if (visitedComponents.find(predecessor) == visitedComponents.end()) { - visitedComponents.insert(predecessor); - queue.push(predecessor); - } - } - } - return visitedComponents; - } - - const std::set & components() const { return components_; } - - private: - - int index{0}; - - std::stack stack; - - std::unordered_map nodeInfo; - - std::set components_; - - private: - - bool visited(T * node) { return nodeInfo[node].dfsId > 0; } -}; - -template -int TarjanAnalysis::StronglyConnectedComponent::idCounter = 0; - -} -} -} - -#endif // DG_LLVM_TARJANANALYSIS_H diff --git a/include/dg/llvm/DataDependence/DataDependence.h b/include/dg/llvm/DataDependence/DataDependence.h deleted file mode 100644 index 7910048d3..000000000 --- a/include/dg/llvm/DataDependence/DataDependence.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef _LLVM_DG_RD_H_ -#define _LLVM_DG_RD_H_ - -#include -#include -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/DataDependence/DataDependence.h" - -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h" - -namespace dg { -namespace dda { - -class LLVMReadWriteGraphBuilder; - -class LLVMDataDependenceAnalysis -{ - const llvm::Module *m; - dg::LLVMPointerAnalysis *pta; - const LLVMDataDependenceAnalysisOptions _options; - LLVMReadWriteGraphBuilder *builder{nullptr}; - std::unique_ptr DDA{nullptr}; - - LLVMReadWriteGraphBuilder *createBuilder(); - DataDependenceAnalysis *createDDA(); - -public: - - LLVMDataDependenceAnalysis(const llvm::Module *m, - dg::LLVMPointerAnalysis *pta, - const LLVMDataDependenceAnalysisOptions& opts = {}) - : m(m), pta(pta), _options(opts), builder(createBuilder()) {} - - ~LLVMDataDependenceAnalysis(); - - void buildGraph() { - assert(builder); - assert(pta); - - DDA.reset(createDDA()); - } - - void run() { - if (!DDA) { - buildGraph(); - } - - assert(DDA); - DDA->run(); - } - - const LLVMDataDependenceAnalysisOptions& getOptions() const { return _options; } - - ReadWriteGraph *getGraph() { return DDA->getGraph(); } - RWNode *getNode(const llvm::Value *val); - const RWNode *getNode(const llvm::Value *val) const; - const llvm::Value *getValue(const RWNode *node) const; - - bool isUse(const llvm::Value *val) const { - auto nd = getNode(val); - return nd && nd->isUse(); - } - - bool isDef(const llvm::Value *val) const { - auto nd = getNode(val); - return nd && nd->isDef(); - } - - std::vector getDefinitions(RWNode *where, RWNode *mem, - const Offset& off, const Offset& len) { - return DDA->getDefinitions(where, mem, off, len); - } - - std::vector getDefinitions(RWNode *use) { - return DDA->getDefinitions(use); - } - - std::vector getDefinitions(llvm::Instruction *where, llvm::Value *mem, - const Offset& off, const Offset& len) { - auto whereN = getNode(where); - assert(whereN); - auto memN = getNode(mem); - assert(memN); - return DDA->getDefinitions(whereN, memN, off, len); - } - - std::vector getDefinitions(llvm::Value *use) { - auto node = getNode(use); - assert(node); - return getDefinitions(node); - } - - // return instructions that define the given value - // (the value must read from memory, e.g. LoadInst) - std::vector getLLVMDefinitions(llvm::Value *use); - std::vector getLLVMDefinitions(llvm::Instruction *where, - llvm::Value *mem, - const Offset& off, - const Offset& len); - - DataDependenceAnalysis *getDDA() { return DDA.get(); } - const DataDependenceAnalysis *getDDA() const { return DDA.get(); } -}; - - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h b/include/dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h deleted file mode 100644 index b74f18a6c..000000000 --- a/include/dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef DG_LLVM_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ -#define DG_LLVM_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ - -#include "dg/llvm/LLVMAnalysisOptions.h" -#include "dg/DataDependence/DataDependenceAnalysisOptions.h" - -namespace dg { - -struct LLVMDataDependenceAnalysisOptions : - public LLVMAnalysisOptions, DataDependenceAnalysisOptions -{ - bool threads{false}; - - LLVMDataDependenceAnalysisOptions() { - // setup models for standard functions - - /// - // Memory block functions - /// - // memcpy defines mem. pointed to by operand 0 from the offset 0 - // to the offset given by the operand 2 - functionModelAddDef("memcpy", {0, Offset(0), 2}); - functionModelAddUse("memcpy", {1, Offset(0), 2}); - functionModelAddDef("__memcpy_chk", {0, Offset(0), 2}); - functionModelAddUse("__memcpy_chk", {1, Offset(0), 2}); - functionModelAddDef("llvm.memcpy.p0i8.p0i8.i64", {0, Offset(0), 2}); - functionModelAddUse("llvm.memcpy.p0i8.p0i8.i64", {1, Offset(0), 2}); - functionModelAddDef("llvm.memcpy.p0i8.p0i8.i32", {0, Offset(0), 2}); - functionModelAddUse("llvm.memcpy.p0i8.p0i8.i32", {1, Offset(0), 2}); - - functionModelAddDef("memmove", {0, Offset(0), 2}); - functionModelAddUse("memmove", {1, Offset(0), 2}); - - functionModelAddDef("memset", {0, Offset(0), 2}); - - functionModelAddUse("memcmp", {0, Offset(0), 2}); - functionModelAddUse("memcmp", {1, Offset(0), 2}); - - /// - // String handling functions - /// - functionModelAddUse("strlen", {0, Offset(0), Offset::getUnknown()}); - functionModelAddUse("strchr", {0, Offset(0), Offset::getUnknown()}); - functionModelAddUse("strrchr", {0, Offset(0), Offset::getUnknown()}); - - functionModelAddDef("strcpy", {0, Offset(0), Offset::getUnknown()}); - functionModelAddUse("strcpy", {1, Offset(0), Offset::getUnknown()}); - functionModelAddDef("strncpy", {0, Offset(0), 2}); - functionModelAddUse("strncpy", {1, Offset(0), 2}); - }; -}; - -} // namespace dg - -#endif // DG_LLVM_DATA_DEPENDENCE_ANALYSIS_OPTIONS_H_ diff --git a/include/dg/llvm/DefUse/DefUse.h b/include/dg/llvm/DefUse/DefUse.h deleted file mode 100644 index 80adb827b..000000000 --- a/include/dg/llvm/DefUse/DefUse.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef _LLVM_DEF_USE_ANALYSIS_H_ -#define _LLVM_DEF_USE_ANALYSIS_H_ - -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/legacy/DataFlowAnalysis.h" -#include "dg/llvm/DataDependence/DataDependence.h" - -using dg::dda::LLVMDataDependenceAnalysis; - -namespace llvm { - class DataLayout; - class ConstantExpr; -}; - -namespace dg { - -class LLVMDependenceGraph; -class LLVMNode; - -class LLVMDefUseAnalysis : public legacy::DataFlowAnalysis -{ - LLVMDependenceGraph *dg; - LLVMDataDependenceAnalysis *RD; - LLVMPointerAnalysis *PTA; - const llvm::DataLayout *DL; - -public: - LLVMDefUseAnalysis(LLVMDependenceGraph *dg, - LLVMDataDependenceAnalysis *rd, - LLVMPointerAnalysis *pta); - - ~LLVMDefUseAnalysis() { delete DL; } - - /* virtual */ - bool runOnNode(LLVMNode *node, LLVMNode *prev); -private: - void addDataDependencies(LLVMNode *node); - - void handleLoadInst(llvm::LoadInst *, LLVMNode *); - void handleCallInst(LLVMNode *); - void handleInlineAsm(LLVMNode *callNode); - void handleIntrinsicCall(LLVMNode *callNode, llvm::CallInst *CI); - void handleUndefinedCall(LLVMNode *callNode, llvm::CallInst *CI); -}; - -} // namespace dg - -#endif // _LLVM_DEF_USE_ANALYSIS_H_ diff --git a/include/dg/llvm/Dominators/Dominators.h b/include/dg/llvm/Dominators/Dominators.h deleted file mode 100644 index 081c2f8fe..000000000 --- a/include/dg/llvm/Dominators/Dominators.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef _DG_DOMINATORS_H_ -#define _DG_DOMINATORS_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "BBlock.h" -#include "analysis/DominanceFrontiers.h" - -namespace dg { -namespace analysis { - - -/** - * Calculates dominators using LLVM framework - * Template parameters: - * NodeT - * CalculateDF = should dominance frontiers be calculated, too? - */ -template -class Dominators -{ -private: - using BlockT = BBlock; - using CFMapT = std::unordered_map>; - using BMapT = std::unordered_map>; - -public: - void calculate(CFMapT& functions_blocks, const BMapT& all_blocks) - { - using namespace llvm; - - - for (auto& pair : functions_blocks){ - - Function& f = *const_cast(pair.first); - - DominatorTree dt; -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR < 9)) - // compute dominator tree for this function - dt.runOnFunction(f); -#else - dt.recalculate(f); -#endif - auto it = all_blocks.find(dt.getRoot()); - assert( it != all_blocks.end() && "root block must exist"); - BlockT *root = it->second.get(); - auto& blocks = pair.second; - for (auto& block : blocks) { - BasicBlock *llvm_block = const_cast(block.first); - BlockT *basic_block = block.second; - - DomTreeNode *N = dt.getNode(llvm_block); - if (!N) - continue; - - DomTreeNode *idom = N->getIDom(); - BasicBlock *idomBB = idom ? idom->getBlock() : nullptr; - - for (const auto& dom : N->getChildren()) { - const BasicBlock *dom_llvm_block = dom->getBlock(); - const auto it = all_blocks.find(static_cast(dom_llvm_block)); - assert( it != all_blocks.end() && "Do not have constructed domBB" ); - const BlockT *dom_block = it->second.get(); - if (dom_block != root) - basic_block->addDominator(const_cast(dom_block)); - } - - if (idomBB) { - auto it = all_blocks.find(idomBB); - assert( it != all_blocks.end() && "Do not have constructed BB" ); - BlockT *db = it->second.get(); - basic_block->setIDom(db); - } else { - if (basic_block != root) - basic_block->setIDom(root); - } - } - - if (CalculateDF) { - analysis::DominanceFrontiers dfrontiers; - if (root) - dfrontiers.compute(root); - } - } - } -}; - -} -} - -#endif /* _DG_DOMINATORS_H_ */ diff --git a/include/dg/llvm/ForkJoin/ForkJoin.h b/include/dg/llvm/ForkJoin/ForkJoin.h deleted file mode 100644 index f062c4de2..000000000 --- a/include/dg/llvm/ForkJoin/ForkJoin.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef DG_FORK_JOIN_ANALYSIS_H_ -#define DG_FORK_JOIN_ANALYSIS_H_ - -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" - -namespace dg { - -/// -// Analyse which functions are spawned by threads -// and which threads are joined by joins. -class ForkJoinAnalysis { - LLVMPointerAnalysis *_PTA{nullptr}; - //const llvm::Module *_M{nullptr}; - -public: - ForkJoinAnalysis(//const llvm::Module *M, - LLVMPointerAnalysis *PTA) - : _PTA(PTA)/*, _M(M)*/ {}; - - /// Take llvm::Value which is a call to pthread_join - // and return a vector of values that (may) spawn a thread - // that may be joined by this join. - std::vector matchJoin(const llvm::Value*); - - /// Take llvm::Value which is a call to pthread_join - // and return a vector of functions that may have been joined - // by this join. - std::vector joinFunctions(const llvm::Value*); -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/GraphBuilder.h b/include/dg/llvm/GraphBuilder.h deleted file mode 100644 index f741cf12d..000000000 --- a/include/dg/llvm/GraphBuilder.h +++ /dev/null @@ -1,286 +0,0 @@ -#ifndef DG_GRAPHBUILDER_H_ -#define DG_GRAPHBUILDER_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/CallGraph/CallGraph.h" -#include "dg/ADT/SetQueue.h" - -namespace dg { - -template -class NodesSeq { - // we can optimize this later... - std::vector nodes; - NodeT *representant{nullptr}; - -public: - NodesSeq(const std::initializer_list& lst) { - if (lst.size() > 0) { - nodes.insert(nodes.end(), lst.begin(), lst.end()); - representant = *lst.begin(); - } - } - - NodesSeq(NodesSeq&&) = default; - NodesSeq(const NodesSeq&) = default; - - NodeT *setRepresentant(NodeT *r) { - representant = r; - } - - NodeT *getRepresentant() const { - return representant; - } - - bool empty() const { return nodes.empty(); } - - auto begin() -> decltype(nodes.begin()) { return nodes.begin(); } - auto end() -> decltype(nodes.end()) { return nodes.end(); } - auto begin() const -> decltype(nodes.begin()) { return nodes.begin(); } - auto end() const -> decltype(nodes.end()) { return nodes.end(); } -}; - - -template -class GraphBuilder { - struct SubgraphInfo { - using BlocksMappingT - = std::unordered_map; - - SubgraphT& subgraph; - BlocksMappingT blocks{}; - - SubgraphInfo(SubgraphT& s) : subgraph(s) {} - SubgraphInfo(SubgraphInfo&&) = default; - SubgraphInfo(const SubgraphInfo&) = delete; - }; - - using GlobalsT = std::vector; - using NodesMappingT = std::unordered_map>; - using ValuesMappingT = std::unordered_map; - using SubgraphsMappingT = std::unordered_map; - - const llvm::Module *_module; - - SubgraphsMappingT _subgraphs; - NodesMappingT _nodes; - ValuesMappingT _nodeToValue; - GlobalsT _globals; - - void buildCFG(SubgraphInfo& subginfo) { - for (auto& it : subginfo.blocks) { - auto llvmblk = it.first; - auto bblock = it.second; - - for (auto *succ : successors(llvmblk)) { - auto succit = subginfo.blocks.find(succ); - assert((succit != subginfo.blocks.end()) - && "Do not have the block built"); - - bblock->addSuccessor(succit->second); - } - } - } - - void buildGlobals() { - DBG_SECTION_BEGIN(dg, "Building globals"); - - for (auto& G : _module->globals()) { - // every global node is like memory allocation - auto cur = buildNode(&G); - _globals.insert(_globals.end(), cur.begin(), cur.end()); - } - - DBG_SECTION_END(dg, "Building globals done"); - } - - -protected: - - NodesSeq buildNode(const llvm::Value *val) { - auto it = _nodes.find(val); - if (it != _nodes.end()) { - return it->second; - } - - const auto& nds = createNode(val); - assert((nds.getRepresentant() || nds.empty()) - && "Built node sequence has no representant"); - - if (auto *repr = nds.getRepresentant()) { - _nodes.emplace(val, std::move(nds)); - - assert((_nodeToValue.find(repr) == _nodeToValue.end()) - && "Mapping a node that we already have"); - _nodeToValue[repr] = val; - } - - return nds; - } - - BBlockT& buildBBlock(const llvm::BasicBlock& B, SubgraphInfo& subginfo) { - auto& bblock = createBBlock(&B, subginfo.subgraph); - assert(subginfo.blocks.find(&B) == subginfo.blocks.end() - && "Already have this basic block"); - subginfo.blocks[&B] = &bblock; - - for (auto& I : B) { - for (auto *node : buildNode(&I)) { - bblock.append(node); - } - } - - return bblock; - } - - void buildSubgraph(const llvm::Function& F) { - using namespace llvm; - - DBG_SECTION_BEGIN(dg, "Building the subgraph for " << F.getName().str()); - auto subgit = _subgraphs.find(&F); - assert(subgit != _subgraphs.end() && "Do not have that subgraph"); - - auto& subginfo = subgit->second; - - DBG(dg, "Building basic blocks of " << F.getName().str()); - // do a walk through basic blocks such that all predecessors of - // a block are searched before the block itself - // (operands must be created before their use) - ADT::SetQueue> queue; - auto &entry = F.getEntryBlock(); - queue.push(&entry); - - while (!queue.empty()) { - auto *cur = queue.pop(); - - buildBBlock(*cur, subginfo); - - for (auto *succ : successors(cur)) { - queue.push(succ); - } - } - - DBG(dg, "Building CFG"); - buildCFG(subginfo); - - DBG_SECTION_END(dg, "Building the subgraph done"); - } - - void buildAllFuns() { - DBG(dg, "Building all functions from LLVM module"); - for (auto& F : *_module) { - if (F.isDeclaration()) { - continue; - } - assert(_subgraphs.find(&F) == _subgraphs.end() - && "Already have that subgraph"); - auto& subg = createSubgraph(&F); - _subgraphs.emplace(&F, subg); - } - - // now do the real thing - for (auto& F : *_module) { - if (!F.isDeclaration()) { - buildSubgraph(F); - } - } - } - - void buildFunsFromCG(llvmdg::CallGraph *cg) { - const auto& funs = cg->functions(); - // we should have at least the entry fun - assert(!funs.empty() && "No function in call graph"); - - for (auto *F : funs) { - DBG(dg, "Building functions based on call graph information"); - assert(_subgraphs.find(F) == _subgraphs.end() - && "Already have that subgraph"); - auto& subg = createSubgraph(F); - _subgraphs.emplace(F, subg); - } - - // now do the real thing - for (auto *F : funs) { - if (!F->isDeclaration()) { - buildSubgraph(*F); - } - } - } - - -public: - GraphBuilder(const llvm::Module *m) : _module(m) {} - virtual ~GraphBuilder() = default; - - const llvm::Module *getModule() const { return _module; } - const llvm::DataLayout *getDataLayout() const { return &_module->getDataLayout(); } - - const GlobalsT& getGlobals() const { return _globals; } - - const NodesMappingT& getNodesMapping() const { - return _nodes; - } - - const ValuesMappingT& getValuesMapping() const { - return _nodeToValue; - } - - const SubgraphsMappingT& getSubgraphsMapping() const { - return _subgraphs; - } - - NodeT *getNode(const llvm::Value *v) { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second.getRepresentant(); - } - - const NodeT *getNode(const llvm::Value *v) const { - auto it = _nodes.find(v); - return it == _nodes.end() ? nullptr : it->second.getRepresentant(); - } - - const llvm::Value *getValue(const NodeT *n) const { - auto it = _nodeToValue.find(n); - return it == _nodeToValue.end() ? nullptr : it->second; - } - - SubgraphT *getSubgraph(const llvm::Function *f) { - auto it = _subgraphs.find(f); - return it == _subgraphs.end() ? nullptr : &it->second.subgraph; - } - - const SubgraphT *getSubgraph(const llvm::Function *f) const { - auto it = _subgraphs.find(f); - return it == _subgraphs.end() ? nullptr : &it->second.subgraph; - } - - virtual NodesSeq createNode(const llvm::Value *) = 0; - virtual BBlockT& createBBlock(const llvm::BasicBlock *, SubgraphT&) = 0; - virtual SubgraphT& createSubgraph(const llvm::Function *) = 0; - - void buildFromLLVM(llvmdg::CallGraph *cg = nullptr) { - assert(_module && "Do not have the LLVM module"); - - buildGlobals(); - - // create emtpy subgraphs for each procedure, - // so that calls can use them as operands - - if (cg) { - buildFunsFromCG(cg); - } else { - buildAllFuns(); - } - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/LLVMAnalysisOptions.h b/include/dg/llvm/LLVMAnalysisOptions.h deleted file mode 100644 index dce7754b5..000000000 --- a/include/dg/llvm/LLVMAnalysisOptions.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef DG_LLVM_ANALYSIS_OPTIONS_H_ -#define DG_LLVM_ANALYSIS_OPTIONS_H_ - -#include -#include "dg/AnalysisOptions.h" - -namespace dg { - -struct LLVMAnalysisOptions { - // Number of bytes in objects to track precisely - std::string entryFunction{"main"}; - - LLVMAnalysisOptions& setEntryFunction(const std::string& e) { - entryFunction = e; return *this; - } -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/LLVMDG2Dot.h b/include/dg/llvm/LLVMDG2Dot.h deleted file mode 100644 index e0d039503..000000000 --- a/include/dg/llvm/LLVMDG2Dot.h +++ /dev/null @@ -1,333 +0,0 @@ -#ifndef DG_LLVMDG2DOT_H_ -#define DG_LLVMDG2DOT_H_ - -#include -#include -#include -#include - -#include "dg/DG2Dot.h" -#include "dg/llvm/LLVMNode.h" - -#include -SILENCE_LLVM_WARNINGS_PUSH -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR <= 4)) -#include "llvm/DebugInfo.h" //DIScope -#else -#include "llvm/IR/DebugInfo.h" //DIScope -#endif -SILENCE_LLVM_WARNINGS_POP - -using namespace dg; -namespace dg { -namespace debug { - -/* -static std::ostream& operator<<(std::ostream& os, const analysis::Offset& off) -{ - if (off.offset == Offset::UNKNOWN) - os << "UNKNOWN"; - else - os << off.offset; - - return os; -} -*/ - -namespace { -static inline std::ostream& printLLVMVal(std::ostream& os, const llvm::Value *val) { - if (!val) { - os << "(null)"; - return os; - } - - std::ostringstream ostr; - llvm::raw_os_ostream ro(ostr); - - if (llvm::isa(val)) { - ro << "FUNC " << val->getName(); - } else if (auto B = llvm::dyn_cast(val)) { - ro << B->getParent()->getName() << "::\n"; - ro << "label " << val->getName(); - } else if (auto I = llvm::dyn_cast(val)) { - const auto B = I->getParent(); - if (B) { - ro << B->getParent()->getName() << "::\n"; - } else { - ro << "::\n"; - } - ro << *val; - } else { - ro << *val; - } - - ro.flush(); - - // break the string if it is too long - std::string str = ostr.str(); - if (str.length() > 100) { - str.resize(40); - } - - // escape the " - size_t pos = 0; - while ((pos = str.find('"', pos)) != std::string::npos) { - str.replace(pos, 1, "\\\""); - // we replaced one char with two, so we must shift after the new " - pos += 2; - } - - os << str; - - return os; -} -} // anonymous namespace - -class LLVMDG2Dot : public debug::DG2Dot -{ -public: - - // FIXME: make dg const - LLVMDG2Dot(LLVMDependenceGraph *dg, - uint32_t opts = debug::PRINT_CFG | debug::PRINT_DD | debug::PRINT_CD, - const char *file = NULL) - : debug::DG2Dot(dg, opts, file) {} - - /* virtual */ - std::ostream& printKey(std::ostream& os, llvm::Value *val) - { - return printLLVMVal(os, val); - } - - /* virtual */ - bool checkNode(std::ostream& os, LLVMNode *node) - { - bool err = false; - const llvm::Value *val = node->getKey(); - - if (!val) { - os << "\\nERR: no value in node"; - return true; - } - - if (!node->getBBlock() - && !llvm::isa(val) - && !llvm::isa(val)) { - err = true; - os << "\\nERR: no BB"; - } - - //Print Location in source file. Print it only for LLVM 3.6 and higher. - // The versions before 3.6 had different API, so this is quite - // a workaround, not a real fix. If anybody needs this functionality - // on those versions, fix this :) - if (const llvm::Instruction *I = llvm::dyn_cast(val)) { - const llvm::DebugLoc& Loc = I->getDebugLoc(); -#if ((LLVM_VERSION_MAJOR > 3)\ - || ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR > 6))) - if(Loc) { - os << "\" labelURL=\""; - llvm::raw_os_ostream ross(os); - Loc.print(ross); -#else - if(Loc.getLine() > 0) { - os << "\" labelURL=\""; - llvm::raw_os_ostream ross(os); - //Loc.print(I->getParent()->getContext(), ross); - const llvm::DebugLoc *tmpLoc = &Loc; - int nclosingBrack = 0; - while (tmpLoc) { - llvm::DIScope Scope(tmpLoc->getScope(I->getParent()->getContext())); - ross << Scope.getFilename(); - ross << ':' << tmpLoc->getLine(); - if (tmpLoc->getCol() != 0) - ross << ':' << tmpLoc->getCol(); - - llvm::MDNode *inlineMN = tmpLoc->getInlinedAt(I->getParent()->getContext()); - if (inlineMN) { - llvm::DebugLoc InlinedAtDL = llvm::DebugLoc::getFromDILocation(inlineMN); - if (!InlinedAtDL.isUnknown()) { - ross << " @[ "; - tmpLoc = &InlinedAtDL; - nclosingBrack++; - } - else { - tmpLoc = nullptr; - } - } else { - tmpLoc = nullptr; - } - } - while (nclosingBrack > 0) { - ross << " ]"; - nclosingBrack--; - } -#endif - ross.flush(); - } - } - - return err; - } - - bool dump(const char *new_file = nullptr, - const char *dump_func_only = nullptr) - { - // make sure we have the file opened - if (!ensureFile(new_file)) - return false; - - const std::map& CF = getConstructedFunctions(); - - start(); - - for (auto& F : CF) { - if (dump_func_only && !F.first->getName().equals(dump_func_only)) - continue; - - dumpSubgraph(F.second, F.first->getName().data()); - } - - end(); - - return true; - } - -private: - - void dumpSubgraph(LLVMDependenceGraph *graph, const char *name) - { - dumpSubgraphStart(graph, name); - - for (auto& B : graph->getBlocks()) { - dumpBBlock(B.second); - } - - for (auto& B : graph->getBlocks()) { - dumpBBlockEdges(B.second); - } - - dumpSubgraphEnd(graph); - } -}; - -class LLVMDGDumpBlocks : public debug::DG2Dot -{ -public: - - LLVMDGDumpBlocks(LLVMDependenceGraph *dg, - uint32_t opts = debug::PRINT_CFG | debug::PRINT_DD | debug::PRINT_CD, - const char *file = NULL) - : debug::DG2Dot(dg, opts, file) {} - - /* virtual - std::ostream& printKey(std::ostream& os, llvm::Value *val) - { - return printLLVMVal(os, val); - } - */ - - /* virtual */ - bool checkNode(std::ostream&, LLVMNode *) - { - return false; // no error - } - - bool dump(const char *new_file = nullptr, - const char *dump_func_only = nullptr) - { - // make sure we have the file opened - if (!ensureFile(new_file)) - return false; - - const std::map& CF = getConstructedFunctions(); - - start(); - - for (auto& F : CF) { - // XXX: this is inefficient, we can get the dump_func_only function - // from the module (F.getParent()->getModule()->getFunction(...) - if (dump_func_only && !F.first->getName().equals(dump_func_only)) - continue; - - dumpSubgraph(F.second, F.first->getName().data()); - } - - end(); - - return true; - } - -private: - - void dumpSubgraph(LLVMDependenceGraph *graph, const char *name) - { - dumpSubgraphStart(graph, name); - - for (auto& B : graph->getBlocks()) { - dumpBlock(B.second); - } - - for (auto& B : graph->getBlocks()) { - dumpBlockEdges(B.second); - } - - dumpSubgraphEnd(graph, false); - } - - void dumpBlock(LLVMBBlock *blk) - { - out << "NODE" << blk << " [label=\""; - - std::ostringstream ostr; - llvm::raw_os_ostream ro(ostr); - - ro << *blk->getKey(); - ro.flush(); - std::string str = ostr.str(); - - unsigned int i = 0; - unsigned int len = 0; - while (str[i] != 0) { - if (len >= 40) { - str[i] = '\n'; - len = 0; - } else - ++len; - - if (str[i] == '\n') - len = 0; - - ++i; - } - - unsigned int slice_id = blk->getSlice(); - if (slice_id != 0) - out << "\\nslice: "<< slice_id << "\\n"; - out << str << "\""; - - if (slice_id != 0) - out << "style=filled fillcolor=greenyellow"; - - out << "]\n"; - } - - void dumpBlockEdges(LLVMBBlock *blk) - { - for (const LLVMBBlock::BBlockEdge& edge : blk->successors()) { - out << "NODE" << blk << " -> NODE" << edge.target - << " [penwidth=2 label=\""<< static_cast(edge.label) << "\"] \n"; - } - - for (const LLVMBBlock *pdf : blk->controlDependence()) { - out << "NODE" << blk << " -> NODE" << pdf - << " [color=blue constraint=false]\n"; - } - } -}; -} /* namespace debug */ -} /* namespace dg */ - -#endif diff --git a/include/dg/llvm/LLVMDGAssemblyAnnotationWriter.h b/include/dg/llvm/LLVMDGAssemblyAnnotationWriter.h deleted file mode 100644 index 11dce62f3..000000000 --- a/include/dg/llvm/LLVMDGAssemblyAnnotationWriter.h +++ /dev/null @@ -1,363 +0,0 @@ -#ifndef _LLVM_DG_ASSEMBLY_ANNOTATION_WRITER_H_ -#define _LLVM_DG_ASSEMBLY_ANNOTATION_WRITER_H_ - - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include - -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR < 5)) - #include - #include -#else // >= 3.5 - #include - #include -#endif -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/LLVMDependenceGraph.h" -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/DataDependence/DataDependence.h" - -namespace dg { -namespace debug { - -class LLVMDGAssemblyAnnotationWriter : public llvm::AssemblyAnnotationWriter -{ - using LLVMDataDependenceAnalysis = dg::dda::LLVMDataDependenceAnalysis; -public: - enum AnnotationOptsT { - // data dependencies - ANNOTATE_DD = 1 << 0, - // forward data dependencies - ANNOTATE_FORWARD_DD = 1 << 1, - // control dependencies - ANNOTATE_CD = 1 << 2, - // points-to information - ANNOTATE_PTR = 1 << 3, - // reaching definitions - ANNOTATE_DEF = 1 << 4, - // post-dominators - ANNOTATE_POSTDOM = 1 << 5, - // comment out nodes that will be sliced - ANNOTATE_SLICE = 1 << 6, - // annotate memory accesses (like ANNOTATE_PTR, - // but with byte intervals) - ANNOTATE_MEMORYACC = 1 << 7, - }; - -private: - - AnnotationOptsT opts; - LLVMPointerAnalysis *PTA; - LLVMDataDependenceAnalysis *DDA; - const std::set *criteria; - std::string module_comment{}; - - void printValue(const llvm::Value *val, - llvm::formatted_raw_ostream& os, - bool nl = false) - { - if (val->hasName()) - os << val->getName().data(); - else - os << *val; - - if (nl) - os << "\n"; - } - - void printPointer(const LLVMPointer& ptr, - llvm::formatted_raw_ostream& os, - const char *prefix = "PTR: ", bool nl = true) { - os << " ; "; - if (prefix) - os << prefix; - - printValue(ptr.value, os); - - os << " + "; - if (ptr.offset.isUnknown()) - os << "?"; - else - os << *ptr.offset; - - if (nl) - os << "\n"; - } - - void printDefSite(const dda::DefSite& ds, - llvm::formatted_raw_ostream& os, - const char *prefix = nullptr, bool nl = false) - { - os << " ; "; - if (prefix) - os << prefix; - - if (ds.target) { - const llvm::Value *val = ds.target->getUserData(); - if (ds.target->isUnknown()) - os << "unknown"; - else - printValue(val, os); - - if (ds.offset.isUnknown()) - os << " bytes |?"; - else - os << " bytes |" << *ds.offset; - - if (ds.len.isUnknown()) - os << " - ?|"; - else - os << " - " << *ds.offset + *ds.len- 1 << "|"; - } else - os << "target is null!"; - - if (nl) - os << "\n"; - - } - - void printMemRegion(const LLVMMemoryRegion& R, - llvm::formatted_raw_ostream& os, - const char *prefix = nullptr, - bool nl = false) { - os << " ; "; - if (prefix) - os << prefix; - - assert(R.pointer.value); - printValue(R.pointer.value, os); - - if (R.pointer.offset.isUnknown()) - os << " bytes [?"; - else - os << " bytes [" << *R.pointer.offset; - - if (R.len.isUnknown()) - os << " - ?]"; - else - os << " - " << *R.pointer.offset + *R.len - 1 << "]"; - - if (nl) - os << "\n"; - } - - void emitNodeAnnotations(LLVMNode *node, llvm::formatted_raw_ostream& os) - { - using namespace llvm; - - if (opts & ANNOTATE_DEF) { - assert(DDA && "No data dependence analysis"); - if (DDA->isUse(node->getValue())) { - os << " ; DEF: "; - const auto& defs = DDA->getLLVMDefinitions(node->getValue()); - if (defs.empty()) { - os << "none (or global)\n"; - } else { - for (auto *def : defs) { - - if (def->hasName()) - os << def->getName(); - else - os << *def; - - os << "(" << def << ")\n"; - } - } - } - } - - if (opts & ANNOTATE_DD) { - for (auto I = node->rev_data_begin(), E = node->rev_data_end(); - I != E; ++I) { - const llvm::Value *d = (*I)->getKey(); - os << " ; DD: "; - - if (d->hasName()) - os << d->getName(); - else - os << *d; - - os << "(" << d << ")\n"; - } - } - - if (opts & ANNOTATE_FORWARD_DD) { - for (auto I = node->data_begin(), E = node->data_end(); - I != E; ++I) { - const llvm::Value *d = (*I)->getKey(); - os << " ; fDD: " << *d << "(" << d << ")\n"; - } - } - - if (opts & ANNOTATE_CD) { - for (auto I = node->rev_control_begin(), E = node->rev_control_end(); - I != E; ++I) { - const llvm::Value *d = (*I)->getKey(); - os << " ; rCD: "; - - if (d->hasName()) - os << d->getName() << "\n"; - else - os << *d << "\n"; - } - } - - if (opts & ANNOTATE_PTR) { - if (PTA) { - llvm::Type *Ty = node->getKey()->getType(); - if (Ty->isPointerTy() || Ty->isIntegerTy()) { - const auto& ps = PTA->getLLVMPointsTo(node->getKey()); - if (!ps.empty()) { - for (const auto& llvmptr : ps) { - printPointer(llvmptr, os); - } - if (ps.hasNull()) { - os << " ; null\n"; - } - if (ps.hasNullWithOffset()) { - os << " ; null + ?\n"; - } - if (ps.hasUnknown()) { - os << " ; unknown\n"; - } - if (ps.hasInvalidated()) { - os << " ; invalidated\n"; - } - } - } - } - } - - if (PTA && (opts & ANNOTATE_MEMORYACC)) { - if (auto I = dyn_cast(node->getValue())) { - if (I->mayReadOrWriteMemory()) { - auto regions = PTA->getAccessedMemory(I); - if (regions.first) { - os << " ; unknown region\n"; - } - for (const auto& mem : regions.second) { - printMemRegion(mem, os, nullptr, true); - } - } - } - } - - if (opts & ANNOTATE_SLICE) { - if (criteria && criteria->count(node) > 0) - os << " ; SLICING CRITERION\n"; - if (node->getSlice() == 0) - os << " ; x "; - } - } - -public: - LLVMDGAssemblyAnnotationWriter(AnnotationOptsT o = ANNOTATE_SLICE, - LLVMPointerAnalysis *pta = nullptr, - LLVMDataDependenceAnalysis *dda = nullptr, - const std::set* criteria = nullptr) - : opts(o), PTA(pta), DDA(dda), criteria(criteria) - { - assert(!(opts & ANNOTATE_PTR) || PTA); - assert(!(opts & ANNOTATE_DEF) || DDA); - } - - void emitModuleComment(const std::string& comment) { - module_comment = comment; - } - - void emitModuleComment(std::string&& comment) { - module_comment = std::move(comment); - } - - void emitFunctionAnnot (const llvm::Function *, - llvm::formatted_raw_ostream &os) override - { - // dump the slicer's setting to the file - // for easier comprehension - static bool didit = false; - if (!didit) { - didit = true; - os << module_comment; - } - } - - void emitInstructionAnnot(const llvm::Instruction *I, - llvm::formatted_raw_ostream& os) override - { - if (opts == 0) - return; - - LLVMNode *node = nullptr; - for (auto& it : getConstructedFunctions()) { - LLVMDependenceGraph *sub = it.second; - node = sub->getNode(const_cast(I)); - if (node) - break; - } - - if (!node) - return; - - emitNodeAnnotations(node, os); - } - - void emitBasicBlockStartAnnot(const llvm::BasicBlock *B, - llvm::formatted_raw_ostream& os) override - { - if (opts == 0) - return; - - for (auto& it : getConstructedFunctions()) { - LLVMDependenceGraph *sub = it.second; - auto& cb = sub->getBlocks(); - auto I = cb.find(const_cast(B)); - if (I != cb.end()) { - LLVMBBlock *BB = I->second; - if (opts & (ANNOTATE_POSTDOM | ANNOTATE_CD)) - os << " ; BB: " << BB << "\n"; - - if (opts & ANNOTATE_POSTDOM) { - for (LLVMBBlock *p : BB->getPostDomFrontiers()) - os << " ; PDF: " << p << "\n"; - - LLVMBBlock *P = BB->getIPostDom(); - if (P && P->getKey()) - os << " ; iPD: " << P << "\n"; - } - - if (opts & ANNOTATE_CD) { - for (LLVMBBlock *p : BB->controlDependence()) - os << " ; CD: " << p << "\n"; - } - } - } - } -}; - -} // namespace debug -} // namespace dg - -// allow combinations of annotation options -inline dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT -operator |(dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT a, - dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT b) { - - using AnT = dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT; - using T = std::underlying_type::type; - return static_cast(static_cast(a) | static_cast(b)); -} - -inline dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT -operator |=(dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT& a, - dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT b) { - - using AnT = dg::debug::LLVMDGAssemblyAnnotationWriter::AnnotationOptsT; - using T = std::underlying_type::type; - a = static_cast(static_cast(a) | static_cast(b)); - return a; -} - -#endif // _LLVM_DG_ANNOTATION_WRITER_H_ - diff --git a/include/dg/llvm/LLVMDGVerifier.h b/include/dg/llvm/LLVMDGVerifier.h deleted file mode 100644 index e8bf9e1d5..000000000 --- a/include/dg/llvm/LLVMDGVerifier.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef _LLVM_DG_VERIFIER_H_ -#define _LLVM_DG_VERIFIER_H_ - -#include "dg/llvm/LLVMDependenceGraph.h" - -namespace llvm { - class Function; - class BasicBlock; -} - -namespace dg { - -// verify if the built dg is ok -// this is friend class of LLVMDependenceGraph, -// so we can do everything! -class LLVMDGVerifier { - const LLVMDependenceGraph *dg; - unsigned int faults; - - void fault(const char *fmt, ...); - void checkMainProc(); - void checkGraph(llvm::Function *, LLVMDependenceGraph *); - void checkBBlock(const llvm::BasicBlock *, LLVMBBlock *); - void checkNode(const llvm::Value *, LLVMNode *); -public: - LLVMDGVerifier(const LLVMDependenceGraph *g) : dg(g), faults(0) {} - bool verify(); -}; - -} - -#endif // _LLVM_DG_VERIFIER_H_ diff --git a/include/dg/llvm/LLVMDependenceGraph.h b/include/dg/llvm/LLVMDependenceGraph.h deleted file mode 100644 index 89ca99601..000000000 --- a/include/dg/llvm/LLVMDependenceGraph.h +++ /dev/null @@ -1,207 +0,0 @@ -#ifndef _LLVM_DEPENDENCE_GRAPH_H_ -#define _LLVM_DEPENDENCE_GRAPH_H_ - -#include -#include - -#include "dg/llvm/ThreadRegions/ControlFlowGraph.h" -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h" - - -// forward declaration of llvm classes -namespace llvm { - class Module; - class Value; - class Function; -} // namespace llvm - -#include "dg/llvm/LLVMNode.h" -#include "dg/DependenceGraph.h" - -namespace dg { - -class LLVMPointerAnalysis; - -//namespace llvmdg { -//class LLVMControlDependenceAnalysis; -//} - - -// FIXME: why PTA is only in the namespace dg -// and this is that nested? Make it consistent... - -namespace dda { class LLVMDataDependenceAnalysis; } - -using dda::LLVMDataDependenceAnalysis; -//using llvmdg::LLVMControlDependenceAnalysis; - -using LLVMBBlock = dg::BBlock; - -/// ------------------------------------------------------------------ -// -- LLVMDependenceGraph -/// ------------------------------------------------------------------ -class LLVMDependenceGraph : public DependenceGraph -{ - // our artificial unified exit block - std::unique_ptr unifiedExitBB{}; - llvm::Function *entryFunction{nullptr}; -public: - LLVMDependenceGraph(bool threads = false) - : gather_callsites(nullptr), threads(threads), module(nullptr), PTA(nullptr) {} - - // free all allocated memory and unref subgraphs - ~LLVMDependenceGraph(); - - // build a nodes and CFG edges from module. - // This method will build also all subgraphs. If entry is nullptr, - // then this methods looks for function named 'main'. - // NOTE: this methods does not compute the dependence edges. - // For that functionality check the LLVMDependenceGraphBuilder. - bool build(llvm::Module *m, llvm::Function *entry = nullptr); - bool build(llvm::Module *m, - LLVMPointerAnalysis *pts = nullptr, - LLVMDataDependenceAnalysis *rda = nullptr, - llvm::Function *entry = nullptr); - - // build DependenceGraph for a function. This will automatically - // build subgraphs of called functions - bool build(llvm::Function *func); - - LLVMDGParameters *getOrCreateParameters(); - LLVMNode *getOrCreateNoReturn(); - LLVMNode *getOrCreateNoReturn(LLVMNode *call); - LLVMNode *getNoReturn() { - auto params = getParameters(); - return params ? params->getNoReturn() : nullptr; - } - - bool addFormalParameter(llvm::Value *val); - bool addFormalGlobal(llvm::Value *val); - - llvm::Module *getModule() const { return module; } - - // if we want to slice according some call-site(s), - // we can gather the relevant call-sites while building - // graph and do not need to recursively find in the graph - // later. This can handle only direct-calls though. If the - // function is called via pointer, it won't be covered by this - // function - void gatherCallsites(const char *name, std::set *callSites) - { - gather_callsites = name; - gatheredCallsites = callSites; - } - - // go through the graph and find all (possible) call-sites - // for a function - // FIXME: can implement via getCallNodes - bool getCallSites(const char *name, std::set *callsites); - // this method takes NULL-terminated array of names - bool getCallSites(const char *names[], std::set *callsites); - bool getCallSites(const std::vector& names, std::set *callsites); - - // FIXME we need remove the callsite from here if we slice away - // the callsite - const std::set& getCallNodes() const { return callNodes; } - std::set& getCallNodes() { return callNodes; } - bool addCallNode(LLVMNode *c) { return callNodes.insert(c).second; } - - // build subgraph for a call node - LLVMDependenceGraph *buildSubgraph(LLVMNode *node); - LLVMDependenceGraph *buildSubgraph(LLVMNode *node, llvm::Function *, bool fork = false); - void addSubgraphGlobalParameters(LLVMDependenceGraph *subgraph); - - void addNoreturnDependencies(LLVMNode *noret, LLVMBBlock *from); - void addNoreturnDependencies(const LLVMControlDependenceAnalysisOptions& opts); - - void computeControlDependencies(const LLVMControlDependenceAnalysisOptions& opts); - - bool verify() const; - - void setThreads(bool threads); - - /* virtual */ - void setSlice(uint64_t sid) - { - DependenceGraph::setSlice(sid); - LLVMNode *entry = getEntry(); - assert(entry); - - // mark even entry node, call-sites are - // control dependent on it - entry->setSlice(sid); - } - - LLVMPointerAnalysis *getPTA() const { return PTA; } - LLVMDataDependenceAnalysis *getDDA() const { return DDA; } - - LLVMNode *findNode(llvm::Value *value) const; - - void addDefUseEdges(); - void computeInterferenceDependentEdges(ControlFlowGraph * controlFlowGraph); - void computeForkJoinDependencies(ControlFlowGraph * controlFlowGraph); - void computeCriticalSections(ControlFlowGraph * controlFlowGraph); -private: - void computePostDominators(bool addPostDomFrontiers = false); - void computeNonTerminationControlDependencies(); - void computeNTSCD(const LLVMControlDependenceAnalysisOptions& opts); - - void computeInterferenceDependentEdges(const std::set &loads, - const std::set &stores); - - std::set getLoadInstructions(const std::set &llvmInstructions) const; - std::set getStoreInstructions(const std::set &llvmInstructions) const; - - std::set getInstructionsOfType(const unsigned opCode, - const std::set &llvmInstructions) const; - - // add formal parameters of the function to the graph - // (graph is a graph of one procedure) - void addFormalParameters(); - - // take action specific to given instruction (while building - // the graph). This is like if the value is a call-site, - // then build subgraph or similar - void handleInstruction(llvm::Value *val, LLVMNode *node, LLVMNode *prevNode); - - // convert llvm basic block to our basic block - // That includes creating all the nodes and adding them - // to this graph and creating the basic block and - // setting first and last instructions - LLVMBBlock *build(llvm::BasicBlock& BB); - - // gather call-sites of functions with given name - // when building the graph - std::set *gatheredCallsites; - const char *gather_callsites; - - bool threads{false}; - - // all callnodes in this graph - forming call graph - std::set callNodes; - - // when we want to slice according to some criterion, - // we may gather the call-sites (good points for criterions) - // while building the graph - llvm::Module *module; - - // analyses needed for building the graph - LLVMPointerAnalysis *PTA; - LLVMDataDependenceAnalysis *DDA; - //LLVMControlDependenceAnalysis *CDA; - - // verifier needs access to private elements - friend class LLVMDGVerifier; -}; - -const std::map& getConstructedFunctions(); - -LLVMNode * -findInstruction(llvm::Instruction * instruction, - const std::map & constructedFunctions); - -llvm::Instruction * castToLLVMInstruction(const llvm::Value * value); -} // namespace dg - -#endif // _DEPENDENCE_GRAPH_H_ diff --git a/include/dg/llvm/LLVMDependenceGraphBuilder.h b/include/dg/llvm/LLVMDependenceGraphBuilder.h deleted file mode 100644 index f1692be80..000000000 --- a/include/dg/llvm/LLVMDependenceGraphBuilder.h +++ /dev/null @@ -1,251 +0,0 @@ -#ifndef _DG_LLVM_DEPENDENCE_GRAPH_BUILDER_H_ -#define _DG_LLVM_DEPENDENCE_GRAPH_BUILDER_H_ - -#include -#include // std::clock - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/LLVMDependenceGraph.h" -#include "dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h" -#include "dg/llvm/DataDependence/DataDependence.h" -#include "dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h" -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "dg/llvm/ControlDependence/LLVMControlDependenceAnalysisOptions.h" - -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#ifdef HAVE_SVF -#include "dg/llvm/PointerAnalysis/SVFPointerAnalysis.h" -#endif -#include "dg/PointerAnalysis/PointerAnalysisFI.h" -#include "dg/PointerAnalysis/PointerAnalysisFS.h" -#include "dg/PointerAnalysis/PointerAnalysisFSInv.h" -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/Offset.h" - -#include "dg/llvm/ThreadRegions/ControlFlowGraph.h" - -namespace llvm { - class Module; - class Function; -} - -namespace dg { -namespace llvmdg { - -struct LLVMDependenceGraphOptions { - LLVMPointerAnalysisOptions PTAOptions{}; - LLVMDataDependenceAnalysisOptions DDAOptions{}; - LLVMControlDependenceAnalysisOptions CDAOptions{}; - - bool verifyGraph{true}; - bool threads{false}; - - std::string entryFunction{"main"}; - - void addAllocationFunction(const std::string& name, - AllocationFunction F) { - PTAOptions.addAllocationFunction(name, F); - DDAOptions.addAllocationFunction(name, F); - } -}; - -class LLVMDependenceGraphBuilder { - llvm::Module *_M; - const LLVMDependenceGraphOptions _options; - std::unique_ptr _PTA{}; - std::unique_ptr _DDA{nullptr}; - std::unique_ptr _CDA{nullptr}; - std::unique_ptr _dg{}; - std::unique_ptr _controlFlowGraph{}; - llvm::Function *_entryFunction{nullptr}; - - struct Statistics { - uint64_t cdaTime{0}; - uint64_t ptaTime{0}; - uint64_t rdaTime{0}; - uint64_t inferaTime{0}; - uint64_t joinsTime{0}; - uint64_t critsecTime{0}; - } _statistics; - - std::clock_t _time_start; - void _timerStart() { _time_start = std::clock(); } - uint64_t _timerEnd() { return (std::clock() - _time_start); } - - void _runPointerAnalysis() { - assert(_PTA && "BUG: No PTA"); - - _timerStart(); - _PTA->run(); - _statistics.ptaTime = _timerEnd(); - } - - void _runDataDependenceAnalysis() { - assert(_DDA && "BUG: No RD"); - - _timerStart(); - _DDA->run(); - _statistics.rdaTime = _timerEnd(); - } - - void _runControlDependenceAnalysis() { - _timerStart(); - //_CDA->run(); - // FIXME: until we get rid of the legacy code, - // use the old way of inserting CD edges directly - // into the dg - _dg->computeControlDependencies(_options.CDAOptions); - _statistics.cdaTime = _timerEnd(); - } - - void _runInterferenceDependenceAnalysis() { - _timerStart(); - _dg->computeInterferenceDependentEdges(_controlFlowGraph.get()); - _statistics.inferaTime = _timerEnd(); - } - - void _runForkJoinAnalysis() { - _timerStart(); - _dg->computeForkJoinDependencies(_controlFlowGraph.get()); - _statistics.joinsTime = _timerEnd(); - } - - void _runCriticalSectionAnalysis() { - _timerStart(); - _dg->computeCriticalSections(_controlFlowGraph.get()); - _statistics.critsecTime = _timerEnd(); - } - - bool verify() const { - return _dg->verify(); - } - -public: - LLVMDependenceGraphBuilder(llvm::Module *M) - : LLVMDependenceGraphBuilder(M, {}) {} - - LLVMDependenceGraphBuilder(llvm::Module *M, - const LLVMDependenceGraphOptions& opts) - : _M(M), _options(opts), - _PTA(createPTA()), - _DDA(new LLVMDataDependenceAnalysis(M, _PTA.get(), - _options.DDAOptions)), - _CDA(new LLVMControlDependenceAnalysis(M, _options.CDAOptions)), - _dg(new LLVMDependenceGraph(opts.threads)), - _controlFlowGraph(_options.threads && !_options.PTAOptions.isSVF() ? // check SVF due to the static cast... - new ControlFlowGraph(static_cast(_PTA.get())) : nullptr), - _entryFunction(M->getFunction(_options.entryFunction)) { - assert(_entryFunction && "The entry function not found"); - } - - LLVMPointerAnalysis *createPTA() { -#ifdef HAVE_SVF - if (_options.PTAOptions.isSVF()) - return new SVFPointerAnalysis(_M, _options.PTAOptions); -#endif - - return new DGLLVMPointerAnalysis(_M, _options.PTAOptions); - } - - LLVMPointerAnalysis *getPTA() { return _PTA.get(); } - LLVMDataDependenceAnalysis *getDDA() { return _DDA.get(); } - - const Statistics& getStatistics() const { return _statistics; } - - // construct the whole graph with all edges - std::unique_ptr&& build() { - // compute data dependencies - _runPointerAnalysis(); - _runDataDependenceAnalysis(); - - // build the graph itself (the nodes, but without edges) - _dg->build(_M, _PTA.get(), _DDA.get(), _entryFunction); - - // insert the data dependencies edges - _dg->addDefUseEdges(); - - // compute and fill-in control dependencies - _runControlDependenceAnalysis(); - - if (_options.threads) { - if (_options.PTAOptions.isSVF()) { - assert(0 && "Threading needs the DG pointer analysis, SVF is not supported yet"); - abort(); - } - _controlFlowGraph->buildFunction(_entryFunction); - _runInterferenceDependenceAnalysis(); - _runForkJoinAnalysis(); - _runCriticalSectionAnalysis(); - } - - // verify if the graph is built correctly - if (_options.verifyGraph && !_dg->verify()) { - _dg.reset(); - return std::move(_dg); - } - - return std::move(_dg); - } - - // Build only the graph with CFG edges. - // No dependencies between instructions are added. - // The dependencies must be filled in by calling computeDependencies() - // later. - // NOTE: this function still runs pointer analysis as it is needed - // for sound construction of CFG in the presence of function pointer calls. - std::unique_ptr&& constructCFGOnly() { - // data dependencies - _runPointerAnalysis(); - - // build the graph itself - _dg->build(_M, _PTA.get(), _DDA.get(), _entryFunction); - - if (_options.threads) { - _controlFlowGraph->buildFunction(_entryFunction); - } - - // verify if the graph is built correctly - if (_options.verifyGraph && !_dg->verify()) { - _dg.reset(); - return std::move(_dg); - } - - return std::move(_dg); - } - - // This method serves to finish the graph construction - // after constructCFGOnly was used to build the graph. - // This function takes the dg (returned from the constructCFGOnly) - // and retains its ownership until it computes the edges. - // Then it returns the ownership back to the caller. - std::unique_ptr&& - computeDependencies(std::unique_ptr&& dg) { - // get the ownership - _dg = std::move(dg); - - // data-dependence edges - _runDataDependenceAnalysis(); - _dg->addDefUseEdges(); - - // fill-in control dependencies - _runControlDependenceAnalysis(); - - if (_options.threads) { - _runInterferenceDependenceAnalysis(); - _runForkJoinAnalysis(); - _runCriticalSectionAnalysis(); - } - - return std::move(_dg); - } - -}; - -} // namespace llvmdg -} // namespace dg - -#endif // _DG_LLVM_DEPENDENCE_GRAPH_BUILDER_H_ diff --git a/include/dg/llvm/LLVMNode.h b/include/dg/llvm/LLVMNode.h deleted file mode 100644 index 96c8aa813..000000000 --- a/include/dg/llvm/LLVMNode.h +++ /dev/null @@ -1,89 +0,0 @@ -#ifndef _LLVM_NODE_H_ -#define _LLVM_NODE_H_ - -#include -#include -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/Node.h" - -namespace dg { - -class LLVMDependenceGraph; -class LLVMNode; - -using LLVMBBlock = dg::BBlock; -using LLVMDGParameter = dg::DGParameterPair; -using LLVMDGParameters = dg::DGParameters; - -/// ------------------------------------------------------------------ -// -- LLVMNode -/// ------------------------------------------------------------------ -class LLVMNode : public Node -{ -#if LLVM_VERSION_MAJOR >= 5 - struct LLVMValueDeleter { - void operator()(llvm::Value *val) const { - val->deleteValue(); - } - }; -#endif - -public: - LLVMNode(llvm::Value *val, bool owns_value = false) - :dg::Node(val) - { - if (owns_value) -#if LLVM_VERSION_MAJOR >= 5 - owned_key = std::unique_ptr(val); -#else - owned_key = std::unique_ptr(val); -#endif - } - - LLVMNode(llvm::Value *val, LLVMDependenceGraph *dg) - : LLVMNode(val) { - setDG(dg); - } - - LLVMDGParameters *getOrCreateParameters() { - auto params = getParameters(); - if (!params) { - params = new LLVMDGParameters(this); - setParameters(params); - } - return params; - } - - llvm::Value *getValue() const { return getKey(); } - - // create new subgraph with actual parameters that are given - // by call-site and add parameter edges between actual and - // formal parameters. The argument is the graph of called function. - // Must be called only when node is call-site. - void addActualParameters(LLVMDependenceGraph *); - void addActualParameters(LLVMDependenceGraph *, llvm::Function *, bool fork = false); - - bool isVoidTy() const { - return getKey()->getType()->isVoidTy(); - } - -private: - - // the owned key will be deleted with this node -#if LLVM_VERSION_MAJOR >= 5 - std::unique_ptr owned_key; -#else - std::unique_ptr owned_key; -#endif -}; - -} // namespace dg - -#endif // _LLVM_NODE_H_ diff --git a/include/dg/llvm/LLVMSlicer.h b/include/dg/llvm/LLVMSlicer.h deleted file mode 100644 index 983cf1406..000000000 --- a/include/dg/llvm/LLVMSlicer.h +++ /dev/null @@ -1,627 +0,0 @@ -#ifndef LLVM_DG_SLICER_H_ -#define LLVM_DG_SLICER_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR < 5)) - #include -#else - #include -#endif - -#include -#include -#include -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/Slicing.h" -#include "dg/llvm/LLVMDependenceGraph.h" -#include "dg/llvm/LLVMNode.h" - -namespace dg { - -class LLVMNode; - -extern std::map constructedFunctions; - -namespace llvmdg { - -template -static void dropAllUses(Val *V) -{ - for (auto I = V->use_begin(), E = V->use_end(); I != E; ++I) { -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR < 5)) - llvm::Value *use = *I; -#else - llvm::Value *use = I->getUser(); -#endif - - // drop the reference to this value - llvm::cast(use)->replaceUsesOfWith(V, nullptr); - } -} - - -class LLVMSlicer : public Slicer -{ -public: - LLVMSlicer(){} - - void keepFunctionUntouched(const char *n) - { - dont_touch.insert(n); - } - - bool removeNode(LLVMNode *node) override - { - using namespace llvm; - - Value *val = node->getKey(); - // if there are any other uses of this value, - // just replace them with undef - val->replaceAllUsesWith(UndefValue::get(val->getType())); - - Instruction *Inst = dyn_cast(val); - if (Inst) { - Inst->eraseFromParent(); - } else { - GlobalVariable *GV = dyn_cast(val); - if (GV) - GV->eraseFromParent(); - } - - return true; - } - - bool removeBlock(LLVMBBlock *block) override - { - assert(block); - - llvm::Value *val = block->getKey(); - if (val == nullptr) - return true; - - llvm::BasicBlock *blk = llvm::cast(val); - for (auto& succ : block->successors()) { - if (succ.label == 255) - continue; - - // don't adjust phi nodes in this block if this is a self-loop, - // we're gonna remove the block anyway - if (succ.target == block) - continue; - - if (llvm::Value *sval = succ.target->getKey()) - adjustPhiNodes(llvm::cast(sval), blk); - } - - // We need to drop the reference to this block in all - // braching instructions that jump to this block. - // See #99 - dropAllUses(blk); - - // we also must drop refrences to instructions that are in - // this block (or we would need to delete the blocks in - // post-dominator order), see #101 - for (llvm::Instruction& Inst : *blk) - dropAllUses(&Inst); - - // finally, erase the block per se - blk->eraseFromParent(); - return true; - } - - // override slice method - uint32_t slice(LLVMNode *start, uint32_t sl_id = 0) - { - (void) sl_id; - (void) start; - - assert(0 && "Do not use this method with LLVM dg"); - return 0; - } - - uint32_t slice(LLVMDependenceGraph *dg, - LLVMNode *start, uint32_t sl_id = 0) - { - // mark nodes for slicing - assert(start || sl_id != 0); - if (start) - sl_id = mark(start, sl_id); - - - std::vector to_erase; - for (auto& F : *dg->getModule()) { - if (dontTouch(F.getName())) - continue; - - auto it = constructedFunctions.find(&F); - if (it == constructedFunctions.end()) { - // remove (defined) functions that we didn't even constructed, - // those are irrelevant in the slice - if (!F.isDeclaration()) { - to_erase.push_back(&F); - } - } else { - LLVMDependenceGraph *subdg = it->second; - sliceGraph(subdg, sl_id); - } - } - for (auto *F : to_erase) { - F->replaceAllUsesWith(llvm::UndefValue::get(F->getType())); - F->deleteBody(); - F->eraseFromParent(); - } - - return sl_id; - } - -private: - /* - void sliceCallNode(LLVMNode *callNode, - LLVMDependenceGraph *graph, uint32_t slice_id) - { - LLVMDGParameters *actualparams = callNode->getParameters(); - LLVMDGParameters *formalparams = graph->getParameters(); - - if (!actualparams) { - assert(!formalparams && "Have only one of params"); - return; // no params - nothing to do - } - - assert(formalparams && "Have only one of params"); - assert(formalparams->size() == actualparams->size()); - - // FIXME slice arguments away - } - - void sliceCallNode(LLVMNode *callNode, uint32_t slice_id) - { - for (LLVMDependenceGraph *subgraph : callNode->getSubgraphs()) - sliceCallNode(callNode, subgraph, slice_id); - } - */ - - static void - adjustPhiNodes(llvm::BasicBlock *pred, llvm::BasicBlock *blk) - { - using namespace llvm; - - for(Instruction& I : *pred) { - PHINode *phi = dyn_cast(&I); - if (phi) { - // don't try remove block that we already removed - int idx = phi->getBasicBlockIndex(blk); - if (idx < 0) - continue; - - // the second argument is DeletePHIIFEmpty. - // We don't want that, since that would make - // dependence graph inconsistent. We'll - // slice it away later, if it's empty - phi->removeIncomingValue(idx, false); - } else { - // phi nodes are always at the beginning of the block - // so if this is the first value that is not PHI, - // there won't be any other and we can bail out - break; - } - } - } - - static inline bool shouldSliceInst(const llvm::Value *val) - { - using namespace llvm; - const Instruction *Inst = dyn_cast(val); - if (!Inst) - return true; - - switch (Inst->getOpcode()) { - case Instruction::Unreachable: -#if 0 - case Instruction::Br: - case Instruction::Switch: - case Instruction::Ret: -#endif - return false; - default: - return true; - } - } - - static LLVMBBlock * - createNewExitBB(LLVMDependenceGraph *graph) - { - using namespace llvm; - - LLVMBBlock *exitBB = new LLVMBBlock(); - - Module *M = graph->getModule(); - LLVMContext& Ctx = M->getContext(); - BasicBlock *block = BasicBlock::Create(Ctx, "safe_return"); - - Value *fval = graph->getEntry()->getKey(); - Function *F = cast(fval); - F->getBasicBlockList().push_back(block); - - // fill in basic block just with return value - ReturnInst *RI; - if (F->getReturnType()->isVoidTy()) - RI = ReturnInst::Create(Ctx, block); - else if (F->getName().equals("main")) - // if this is main, than the safe exit equals to returning 0 - // (it is just for convenience, we wouldn't need to do this) - RI = ReturnInst::Create(Ctx, - ConstantInt::get(Type::getInt32Ty(Ctx), 0), - block); - else - RI = ReturnInst::Create(Ctx, - UndefValue::get(F->getReturnType()), - block); - - LLVMNode *newRet = new LLVMNode(RI); - graph->addNode(newRet); - - exitBB->append(newRet); - exitBB->setKey(block); - exitBB->setDG(graph); - - return exitBB; - } - - static LLVMBBlock* addNewExitBB(LLVMDependenceGraph *graph) - { - // FIXME: don't create new one, create it - // when creating graph and just use that one - LLVMBBlock *newExitBB = createNewExitBB(graph); - graph->setExitBB(newExitBB); - graph->setExit(newExitBB->getLastNode()); - // do not add the block to the graph, - // we'll do it at the end of adjustBBlocksSucessors, - // because this function is called while iterating - // over blocks, so that we won't corrupt the iterator - - return newExitBB; - } - - // when we sliced away a branch of CFG, we need to reconnect it - // to exit block, since on this path we would silently terminate - // (this path won't have any effect on the property anymore) - void adjustBBlocksSucessors(LLVMDependenceGraph *graph, uint32_t slice_id) - { - LLVMBBlock *oldExitBB = graph->getExitBB(); - assert(oldExitBB && "Don't have exit BB"); - - LLVMBBlock *newExitBB = nullptr; - - for (auto& it : graph->getBlocks()) { - const llvm::BasicBlock *llvmBB - = llvm::cast(it.first); - const auto tinst = llvmBB->getTerminator(); - LLVMBBlock *BB = it.second; - - // nothing to do - if (BB->successorsNum() == 0) - continue; - - // if the BB has two successors and one is self-loop and - // the branch inst is going to be removed, then the brach - // that created the self-loop has no meaning to the sliced - // program and this is going to be an unconditional jump - // to the other branch - // NOTE: do this before the next action, to rename the label if needed - if (BB->successorsNum() == 2 - && BB->getLastNode()->getSlice() != slice_id - && !BB->successorsAreSame() && BB->hasSuccessor(BB)) { - - bool found = BB->removeSuccessorsTarget(BB); - // we have two different successors, none of them - // is self-loop and we're slicing away the brach inst? - // This should not happen... - if (!found) { - assert(found && "Self loop did not have self loop..."); - abort(); - } - assert(BB->successorsNum() == 1 && "Should have only one successor"); - - // continue here to rename the only label if needed - } - - // if the BB has only one successor and the terminator - // instruction is going to be sliced away, it means that - // this is going to be an unconditional jump, - // so just make the label 0 - if (BB->successorsNum() == 1 - && BB->getLastNode()->getSlice() != slice_id) { - auto edge = *(BB->successors().begin()); - - // modify the edge - edge.label = 0; - if (edge.target == oldExitBB) { - if (!newExitBB) - newExitBB = addNewExitBB(graph); - - edge.target = newExitBB; - } - - // replace the only edge - BB->removeSuccessors(); - BB->addSuccessor(edge); - - continue; - } - - // when we have more successors, we need to fill in - // jumps under labels that we sliced away - - DGContainer labels; - // go through BBs successors and gather all labels - // from edges that go from this BB. Also if there's - // a jump to return block, replace it with new - // return block - for (const auto& succ : BB->successors()) { - // skip artificial return basic block. - if (succ.label == 255 || succ.target == oldExitBB) - continue; - - labels.insert(succ.label); - } - - // replace missing labels. Label should be from 0 to some max, - // no gaps, so jump to safe exit under missing labels - for (uint8_t i = 0; i < tinst->getNumSuccessors(); ++i) { - if (!labels.contains(i)) { - if (!newExitBB) - newExitBB = addNewExitBB(graph); - -#ifndef NDEBUG - bool ret = -#endif - BB->addSuccessor(newExitBB, i); - assert(ret && "Already had this CFG edge, that is wrong"); - } - } - - // this BB is going to be removed - if (newExitBB) - BB->removeSuccessorsTarget(oldExitBB); - - // if we have all successor edges pointing to the same - // block, replace them with one successor (thus making - // unconditional jump) - if (BB->successorsNum() > 1 && BB->successorsAreSame()) { - LLVMBBlock *succ = BB->successors().begin()->target; - - BB->removeSuccessors(); - BB->addSuccessor(succ, 0); -#ifdef NDEBUG - assert(BB->successorsNum() == 1 - && "BUG: in removeSuccessors() or addSuccessor()"); -#endif - } - -#ifndef NDEBUG - // check the BB - labels.clear(); - for (const auto& succ : BB->successors()) { - assert((!newExitBB || succ.target != oldExitBB) - && "A block has the old BB as successor"); - // we can have more labels with different targets, - // but we can not have one target with more labels - assert(labels.insert(succ.label) && "Already have a label"); - } - - // check that we have all labels without any gep - auto l = labels.begin(); - for (unsigned i = 0; i < labels.size(); ++i) { - // set is ordered, so this must hold - // (as 255 is the last possible label) - assert((*l == 255 || i == *l++) && "Labels have a gap"); - } -#endif - } - - if (newExitBB) { - graph->addBlock(newExitBB->getKey(), newExitBB); - assert(graph->getExitBB() == newExitBB); - // NOTE: do not delete the old block - // because it is the unified BB that is kept in - // unique_ptr, so it will be deleted later automatically. - // Deleting it would lead to double-free - } - } - - void sliceGraph(LLVMDependenceGraph *graph, uint32_t slice_id) - { - // first slice away bblocks that should go away - sliceBBlocks(graph, slice_id); - - // make graph complete - adjustBBlocksSucessors(graph, slice_id); - - // now slice away instructions from BBlocks that left - for (auto I = graph->begin(), E = graph->end(); I != E;) { - LLVMNode *n = I->second; - // shift here, so that we won't corrupt the iterator - // by deleteing the node - ++I; - - // we added this node artificially and - // we don't want to slice it away or - // take any other action on it - if (n == graph->getExit()) - continue; - - ++statistics.nodesTotal; - - // keep instructions like ret or unreachable - // FIXME: if this is ret of some value, then - // the value is undef now, so we should - // replace it by void ref - if (!shouldSliceInst(n->getKey())) - continue; - - /* - if (llvm::isa(n->getKey())) - sliceCallNode(n, slice_id); - */ - - if (n->getSlice() != slice_id) { - removeNode(n); - graph->deleteNode(n); - ++statistics.nodesRemoved; - } - } - - // create new CFG edges between blocks after slicing - reconnectLLLVMBasicBlocks(graph); - - // if we sliced away entry block, our new entry block - // may have predecessors, which is not allowed in the - // LLVM - ensureEntryBlock(graph); - } - - bool dontTouch(const llvm::StringRef& r) - { - for (const char *n : dont_touch) - if (r.equals(n)) - return true; - - return false; - } - - void reconnectBBlock(LLVMBBlock *BB, llvm::BasicBlock *llvmBB) - { - using namespace llvm; - - auto tinst = llvmBB->getTerminator(); - assert((!tinst || BB->successorsNum() <= 2 || llvm::isa(tinst)) - && "BB has more than two successors (and it's not a switch)"); - - if (!tinst) { - // block has no terminator - // It may occur for example if we have: - // - // call error() - // br %exit - // - // The br instruction has no meaning when error() abort, - // but if error is not marked as noreturn, then the br - // will be there and will get sliced, making the block - // unterminated. The same may happen if we remove unconditional - // branch inst - - LLVMContext& Ctx = llvmBB->getContext(); - Function *F = cast(llvmBB->getParent()); - bool create_return = true; - - if (BB->successorsNum() == 1) { - const LLVMBBlock::BBlockEdge& edge = *(BB->successors().begin()); - if (edge.label != 255) { - // don't create return, we created branchinst - create_return = false; - - BasicBlock *succ = cast(edge.target->getKey()); - BranchInst::Create(succ, llvmBB); - } - } - - if (create_return) { - if (BB->successorsNum() != 0) { - assert(BB->successorsNum() == 0 - && "Creating return to BBlock that has successors"); - abort(); - } - - if (F->getReturnType()->isVoidTy()) - ReturnInst::Create(Ctx, llvmBB); - else if (F->getName().equals("main")) - // if this is main, than the safe exit equals to returning 0 - // (it is just for convenience, we wouldn't need to do this) - ReturnInst::Create(Ctx, - ConstantInt::get(Type::getInt32Ty(Ctx), 0), - llvmBB); - else - ReturnInst::Create(Ctx, - UndefValue::get(F->getReturnType()), llvmBB); - - } - - // and that is all we can do here - return; - } - - for (const LLVMBBlock::BBlockEdge& succ : BB->successors()) { - // skip artificial return basic block - if (succ.label == 255) - continue; - - llvm::Value *val = succ.target->getKey(); - assert(val && "nullptr as BB's key"); - llvm::BasicBlock *llvmSucc = llvm::cast(val); - tinst->setSuccessor(succ.label, llvmSucc); - } - - // if the block still does not have terminator - } - - void reconnectLLLVMBasicBlocks(LLVMDependenceGraph *graph) - { - for (auto& it : graph->getBlocks()) { - llvm::BasicBlock *llvmBB - = llvm::cast(it.first); - LLVMBBlock *BB = it.second; - - reconnectBBlock(BB, llvmBB); - } - } - - void ensureEntryBlock(LLVMDependenceGraph *graph) - { - using namespace llvm; - - Value *val = graph->getEntry()->getKey(); - Function *F = cast(val); - - // Function is empty, just bail out - if(F->begin() == F->end()) - return; - - BasicBlock *entryBlock = &F->getEntryBlock(); - - if (pred_begin(entryBlock) == pred_end(entryBlock)) { - // entry block has no predecessor, we're ok - return; - } - - // it has some predecessor, create new one, that will just - // jump on it - LLVMContext& Ctx = graph->getModule()->getContext(); - BasicBlock *block = BasicBlock::Create(Ctx, "single_entry"); - - // jump to the old entry block - BranchInst::Create(entryBlock, block); - - // set it as a new entry by pusing the block to the front - // of the list - F->getBasicBlockList().push_front(block); - - // FIXME: propagate this change to dependence graph - } - - // do not slice these functions at all - std::set dont_touch; -}; - -} // namespace llvmdg -} // namespace dg - -#endif - diff --git a/include/dg/llvm/PointerAnalysis/DGPointerAnalysis.h b/include/dg/llvm/PointerAnalysis/DGPointerAnalysis.h deleted file mode 100644 index 181b8777b..000000000 --- a/include/dg/llvm/PointerAnalysis/DGPointerAnalysis.h +++ /dev/null @@ -1,245 +0,0 @@ -#ifndef _LLVM_DG_POINTS_TO_ANALYSIS_H_ -#define _LLVM_DG_POINTS_TO_ANALYSIS_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/analysis/PointsTo/Pointer.h" -#include "dg/analysis/PointsTo/PointerGraph.h" -#include "dg/analysis/PointsTo/PointerAnalysis.h" -#include "dg/analysis/PointsTo/PointerGraphOptimizations.h" -#include "dg/analysis/PointsTo/PointerAnalysisFI.h" -#include "dg/analysis/PointsTo/PointerAnalysisFS.h" -#include "dg/analysis/PointsTo/PointerAnalysisFSInv.h" - -#include "dg/llvm/analysis/PointsTo/LLVMPointerAnalysisOptions.h" -#include "dg/llvm/analysis/PointsTo/PointerAnalysis.h" -#include "dg/llvm/analysis/PointsTo/PointerGraph.h" -#include "dg/llvm/analysis/PointsTo/LLVMPointsToSet.h" - -namespace dg { - -using analysis::LLVMPointerAnalysisOptions; -using analysis::pta::PointerGraph; -using analysis::pta::PSNode; -using analysis::pta::LLVMPointerGraphBuilder; -using analysis::pta::Pointer; -using analysis::Offset; - -template -class DGLLVMPointerAnalysisImpl : public PTType { - LLVMPointerGraphBuilder *builder; - -public: - DGLLVMPointerAnalysisImpl(PointerGraph *PS, LLVMPointerGraphBuilder *b) - : PTType(PS), builder(b) {} - - DGLLVMPointerAnalysisImpl(PointerGraph *PS, LLVMPointerGraphBuilder *b, - const LLVMPointerAnalysisOptions& opts) - : PTType(PS, opts), builder(b) {} - - // build new subgraphs on calls via pointer - bool functionPointerCall(PSNode *callsite, PSNode *called) override { - using namespace analysis::pta; - const llvm::Function *F - = llvm::dyn_cast(called->getUserData()); - // with vararg it may happen that we get pointer that - // is not to function, so just bail out here in that case - if (!F) - return false; - - if (F->isDeclaration()) { - if (builder->threads()) { - if (F->getName() == "pthread_create") { - builder->insertPthreadCreateByPtrCall(callsite); - return true; - } else if (F->getName() == "pthread_join") { - builder->insertPthreadJoinByPtrCall(callsite); - return true; - } - } - return callsite->getPairedNode()->addPointsTo(analysis::pta::UnknownPointer); - } - - if (!LLVMPointerGraphBuilder::callIsCompatible(callsite, called)) { - return false; - } - - builder->insertFunctionCall(callsite, called); - - // call the original handler that works on generic graphs - PTType::functionPointerCall(callsite, called); - -#ifndef NDEBUG - // check the graph after rebuilding, but do not check for connectivity, - // because we can call a function that will disconnect the graph - if (!builder->validateSubgraph(true)) { - llvm::errs() << "Pointer Subgraph is broken!\n"; - llvm::errs() << "This happend after building this function called via pointer: " - << F->getName() << "\n"; - abort(); - } -#endif // NDEBUG - - return true; // we changed the graph - } - - bool handleFork(PSNode *forkNode, PSNode *called) override { - using namespace llvm; - using namespace dg::analysis::pta; - - assert(called->getType() == PSNodeType::FUNCTION - && "The called value is not a function"); - - PSNodeFork *fork = PSNodeFork::get(forkNode); - builder->addFunctionToFork(called, fork); - -#ifndef NDEBUG - // check the graph after rebuilding, but do not check for connectivity, - // because we can call a function that will disconnect the graph - if (!builder->validateSubgraph(true)) { - const llvm::Function *F - = llvm::cast(called->getUserData()); - llvm::errs() << "Pointer Subgraph is broken!\n"; - llvm::errs() << "This happend after building this function spawned in a thread: " - << F->getName() << "\n"; - abort(); - } -#endif // NDEBUG - - return true; - } - - bool handleJoin(PSNode *joinNode) override { - return builder->matchJoinToRightCreate(joinNode); - } -}; - -class DGLLVMPointerAnalysis : public LLVMPointerAnalysis { - PointerGraph *PS = nullptr; - std::unique_ptr _builder; - - LLVMPointerAnalysisOptions createOptions(const char *entry_func, - uint64_t field_sensitivity, - bool threads = false) - { - LLVMPointerAnalysisOptions opts; - opts.threads = threads; - opts.setFieldSensitivity(field_sensitivity); - opts.setEntryFunction(entry_func); - return opts; - } - - const PointsToSetT& getUnknownPTSet() const { - static const PointsToSetT _unknownPTSet - = PointsToSetT({Pointer{analysis::pta::UNKNOWN_MEMORY, 0}}); - return _unknownPTSet; - } - -public: - - DGLLVMPointerAnalysis(const llvm::Module *m, - const char *entry_func = "main", - uint64_t field_sensitivity = Offset::UNKNOWN, - bool threads = false) - : DGLLVMPointerAnalysis(m, createOptions(entry_func, field_sensitivity, threads)) {} - - DGLLVMPointerAnalysis(const llvm::Module *m, const LLVMPointerAnalysisOptions opts) - : LLVMPointerAnalysis(opts), _builder(new LLVMPointerGraphBuilder(m, opts)) {} - - /// - // Get the node from pointer analysis that holds the points-to set. - // See: getLLVMPointsTo() - PSNode *getPointsTo(const llvm::Value *val) const { - return _builder->getPointsTo(val); - } - - inline bool threads() const { return _builder->threads(); } - - /// - // Get the points-to information for the given LLVM value. - // The return object has methods begin(), end() that can be used - // for iteration over (llvm::Value *, Offset) pairs of the - // points-to set. Moreover, the object has methods hasUnknown() - // and hasNull() that reflect whether the points-to set of the - // LLVM value contains unknown element of null. - LLVMPointsToSet getLLVMPointsTo(const llvm::Value *val) override { - if (auto node = getPointsTo(val)) - return LLVMPointsToSet(node->pointsTo); - else - return LLVMPointsToSet(getUnknownPTSet()); - } - - /// - // This method is the same as getLLVMPointsTo, but it returns - // also the information whether the node of pointer analysis exists - // (opposed to the getLLVMPointsTo, which returns a set with - // unknown element when the node does not exists) - std::pair - getLLVMPointsToChecked(const llvm::Value *val) override { - if (auto node = getPointsTo(val)) - return {true, LLVMPointsToSet(node->pointsTo)}; - else - return {false, LLVMPointsToSet(getUnknownPTSet())}; - } - - const std::vector>& getNodes() - { - return PS->getNodes(); - } - - std::vector getFunctionNodes(const llvm::Function *F) const { - return _builder->getFunctionNodes(F); - } - - PointerGraph *getPS() { return PS; } - const PointerGraph *getPS() const { return PS; } - - LLVMPointerGraphBuilder *getBuilder() { return _builder.get(); } - const LLVMPointerGraphBuilder *getBuilder() const { return _builder.get(); } - - - bool run() override { - if (options.isFSInv()) - _builder->setInvalidateNodesFlag(true); - - buildSubgraph(); - - bool ret = false; - if (options.isFS()) { - // FIXME: make a interface with run() method - DGLLVMPointerAnalysisImpl PTA(PS, _builder.get()); - ret = PTA.run(); - } else if (options.isFI()) { - DGLLVMPointerAnalysisImpl PTA(PS, _builder.get()); - ret = PTA.run(); - } else if (options.isFSInv()) { - DGLLVMPointerAnalysisImpl PTA(PS, _builder.get()); - ret = PTA.run(); - } else { - assert(0 && "Wrong pointer analysis"); - abort(); - } - - return ret; - } - - // this method creates PointerAnalysis object and returns it. - // It is alternative to run() method, but it does not delete all - // the analysis data as the run() (like memory objects and so on). - // run() preserves only PointerGraph and the builder - template - analysis::pta::PointerAnalysis *createPTA() - { - buildSubgraph(); - return new DGLLVMPointerAnalysisImpl(PS, _builder.get()); - } -}; - -} // namespace dg - -#endif // _LLVM_DG_POINTS_TO_ANALYSIS_H_ diff --git a/include/dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h b/include/dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h deleted file mode 100644 index 7520d10b0..000000000 --- a/include/dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef DG_LLVM_POINTER_ANALYSIS_OPTIONS_H_ -#define DG_LLVM_POINTER_ANALYSIS_OPTIONS_H_ - -#include "dg/llvm/LLVMAnalysisOptions.h" -#include "dg/PointerAnalysis/PointerAnalysisOptions.h" - -namespace dg { - -struct LLVMPointerAnalysisOptions : public LLVMAnalysisOptions, PointerAnalysisOptions -{ - enum class AnalysisType { fi, fs, inv, svf } analysisType{AnalysisType::fi}; - - bool threads{false}; - - bool isFS() const { return analysisType == AnalysisType::fs; } - bool isFSInv() const { return analysisType == AnalysisType::inv; } - bool isFI() const { return analysisType == AnalysisType::fi; } - bool isSVF() const { return analysisType == AnalysisType::svf; } -}; - -} // namespace dg - -#endif diff --git a/include/dg/llvm/PointerAnalysis/LLVMPointsToSet.h b/include/dg/llvm/PointerAnalysis/LLVMPointsToSet.h deleted file mode 100644 index 1d3d149d3..000000000 --- a/include/dg/llvm/PointerAnalysis/LLVMPointsToSet.h +++ /dev/null @@ -1,479 +0,0 @@ -#ifndef _LLVM_DG_POINTS_TO_SET_H_ -#define _LLVM_DG_POINTS_TO_SET_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/PointerAnalysis/PointsToSet.h" - -namespace dg { - -using pta::PointsToSetT; -using pta::PSNode; - -/// -// LLVM pointer -// - value is the allocation site -// - offset is offset into the memory -struct LLVMPointer { - llvm::Value *value; - Offset offset; - - LLVMPointer(llvm::Value *val, Offset o) - : value(val), offset(o) { - assert(val && "nullptr passed as value"); - } - - bool operator==(const LLVMPointer& rhs) const { - return value == rhs.value && offset == rhs.offset; - } - - /// - // Memory locations described by this pointer cover - // (are a supperset) the memory locations of the rhs pointer. - bool covers(const LLVMPointer& rhs) const { - return value == rhs.value && - (offset.isUnknown() || offset == rhs.offset); - } -}; - - -/// -// LLVM memory region -// Pointer + length of referenced memory -struct LLVMMemoryRegion { - LLVMPointer pointer; - Offset len; - - LLVMMemoryRegion(const LLVMPointer& ptr, const Offset l) - : pointer(ptr), len(l) {} - - LLVMMemoryRegion(llvm::Value *val, const Offset off, const Offset l) - : pointer(val, off), len(l) {} -}; - -/// -// A set of memory regions -// XXX: we should create more efficient implementation. -class LLVMMemoryRegionSet { - struct OffsetPair { - const Offset offset{0}; - const Offset len{0}; - - OffsetPair() = default; - OffsetPair(const OffsetPair& rhs) = default; - OffsetPair(const Offset o, const Offset l) - : offset(o), len(l) {} - - bool overlaps(const OffsetPair interval) const { - return overlaps(interval.offset, interval.len); - } - - bool overlaps(const Offset o, const Offset l) const { - if (o.isUnknown() || offset.isUnknown()) - return true; - - if (o < offset) { - if (l.isUnknown() || o + l >= offset) { - return true; - } - } else { - return o <= offset + len; - } - - return false; - } - - bool coveredBy(const OffsetPair& rhs) const { - return coveredBy(rhs.offset, rhs.len); - } - - bool coveredBy(const Offset o, const Offset l) const { - assert(!o.isUnknown() && !offset.isUnknown()); - // we allow len == UNKNOWN and treat it as infinity - - return (o <= offset && - ((l.isUnknown() && len.isUnknown()) || - l.isUnknown() || l >= len)); - } - - bool extends(const OffsetPair& rhs) const { - return rhs.coveredBy(*this); - } - - bool extends(const Offset o, const Offset l) const { - return extends({o, l}); - } - }; - - using MappingT = std::map>; - - // intervals of bytes for each memory - // (llvm::Value corresponding to the allocation) - MappingT _regions; - - std::pair _extend(const OffsetPair interval, - const Offset off, const Offset len) { - assert(interval.overlaps(off, len)); - assert(!off.isUnknown()); - - Offset o = interval.offset; - Offset l = interval.len; - - if (off < interval.len) - o = off; - if (interval.len < len || len.isUnknown()) { - l = len; - } - - return std::make_pair(o, l); - } - - const std::vector *_get(llvm::Value *v) const { - auto it = _regions.find(v); - return it == _regions.end() ? nullptr : &it->second; - } - -public: - // Add a memory region to this set - // - // not very efficient, but we will use it only - // for transfering the results, so it should be fine - void add(llvm::Value *mem, const Offset o, const Offset l) { - auto& R = _regions[mem]; - // we do not know the bytes in this region - if (o.isUnknown()) { - R.clear(); - R.emplace_back(o, o); - return; - } - - assert(!o.isUnknown()); - - for (auto& interval : R) { - if (interval.offset.isUnknown() || - interval.extends(o, l)) { - return; // nothing to be done - } - } - - - // join all overlapping intervals - Offset newO = o; - Offset newL = l; - for (auto& interval : R) { - if (interval.overlaps(newO, newL)) { - std::tie(newO, newL) = _extend(interval, newO, newL); - } - } - - std::vector tmp; - tmp.reserve(R.size()); - - for (auto& interval : R) { - // get rid of covered intervals - if (interval.coveredBy(newO, newL)) - continue; - - - - // if intervals overlap, join them, - // otherwise keep the original interval - assert(!interval.overlaps(newO, newL)); - tmp.push_back(interval); - } - - tmp.emplace_back(newO, newL); - tmp.swap(R); - -#ifndef NDEBUG - for (auto& interval : R) { - assert(((interval.offset == newO && interval.len == newL) || - !interval.overlaps(newO, newL)) - && "Joined intervals incorrectly"); - } -#endif // NDEBUG - } - - // XXX: inefficient - bool overlaps(const LLVMMemoryRegionSet& rhs) const { - for (auto& it : rhs._regions) { - auto *our = _get(it.first); - if (!our) { - continue; - } - - for (auto& interval : *our) { - for (auto& interval2 : it.second) { - if (interval.overlaps(interval2)) - return true; - } - } - } - - return false; - } - - class const_iterator { - size_t pos{0}; - typename MappingT::const_iterator it; - - const_iterator(const MappingT::const_iterator I) : it(I) {} - - public: - LLVMMemoryRegion operator*() const { - return LLVMMemoryRegion{it->first, - it->second[pos].offset, - it->second[pos].len}; - } - - const_iterator& operator++() { - ++pos; - if (pos >= it->second.size()) { - ++it; - pos = 0; - } - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - bool operator==(const const_iterator& rhs) const { - return it == rhs.it && pos == rhs.pos; - } - - bool operator!=(const const_iterator& rhs) const { - return !operator==(rhs); - } - - friend class LLVMMemoryRegionSet; - }; - - const_iterator begin() const { return const_iterator(_regions.begin()); } - const_iterator end() const { return const_iterator(_regions.end()); } -}; - -/// -// Implementation of LLVMPointsToSet -class LLVMPointsToSetImpl { -public: - /// - // NOTE: this may not be O(1) operation - virtual bool hasUnknown() const = 0; - virtual bool hasNull() const = 0; - virtual bool hasNullWithOffset() const = 0; - virtual bool hasInvalidated() const = 0; - virtual size_t size() const = 0; - - virtual LLVMPointer getKnownSingleton() const = 0; - - // for iterator implementation - // XXX: merge position() and end()? - virtual int position() const = 0; // for comparision - virtual bool end() const = 0; // have done iteration? - virtual void shift() = 0; // iterate - virtual LLVMPointer get() const = 0; // dereference - - virtual ~LLVMPointsToSetImpl() = default; -}; - -/// -// Wrapper for PointsToSet with iterators that yield LLVMPointer, -// so that mapping pointer analysis results to LLVM is opaque for -// the user. The special nodes like unknown memory and null -// are not yield by the iterators. Instead, the class has methods -// hasUnknown() and hasNull() to express these properties. -// This also means that it is possible that iterating over the -// set yields no elements, but empty() == false -// (the set contains only unknown or null elements) -// XXX: would it be easier and actually more efficient -// just to return a std::vector instead -// of all this wrapping? -class LLVMPointsToSet { - std::unique_ptr _impl{}; - -public: - class const_iterator { - LLVMPointsToSetImpl *impl{nullptr}; - - // impl = null means end iterator - void _check_end() { - assert(impl); - if (impl->end()) - impl = nullptr; - } - - const_iterator(LLVMPointsToSetImpl *impl = nullptr) : impl(impl) { - // the iterator (impl) may have been shifted during initialization - // to the end() (because it contains no llvm::Values or the - // ptset is empty) - if (impl) - _check_end(); - } - - public: - const_iterator& operator++() { - impl->shift(); - _check_end(); - return *this; - } - - const_iterator operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - - LLVMPointer operator*() const { - return impl->get(); - } - - bool operator==(const const_iterator& rhs) const { - if (!impl) - return !rhs.impl; - if (!rhs.impl) - return !impl; - - assert(!impl->end() && rhs.impl->end()); - assert(impl == rhs.impl && "Compared unrelated iterators"); // catch bugs - llvm::errs() << "CMP" << impl << "+" << impl->position() << " == " - << rhs.impl << "+" << rhs.impl->position() << "\n"; - return impl == rhs.impl && impl->position() == rhs.impl->position(); - } - - bool operator!=(const const_iterator& rhs) const { return !operator==(rhs);} - - friend class LLVMPointsToSet; - }; - - LLVMPointsToSet(LLVMPointsToSetImpl *impl) : _impl(impl) {} - LLVMPointsToSet() = default; - LLVMPointsToSet(LLVMPointsToSet&&) = default; - LLVMPointsToSet& operator=(LLVMPointsToSet&&) = default; - - /// - // NOTE: this may not be O(1) operation - bool hasUnknown() const { return _impl->hasUnknown(); } - bool hasNull() const { return _impl->hasNull(); } - bool hasNullWithOffset() const { return _impl->hasNullWithOffset(); } - bool hasInvalidated() const { return _impl->hasInvalidated(); } - bool empty() const { return _impl->size() == 0; } - size_t size() const { return _impl->size(); } - - bool isSingleton() const { return _impl->size() == 1; } - bool isKnownSingleton() const { return isSingleton() - && !_impl->hasUnknown() - && !_impl->hasNull() - && !_impl->hasInvalidated(); } - - // matches {unknown} - bool isUnknownSingleton() const { return isSingleton() && hasUnknown(); } - - LLVMPointer getKnownSingleton() const { return _impl->getKnownSingleton(); } - - const_iterator begin() const { return const_iterator(_impl.get());} - const_iterator end() const { return const_iterator(nullptr); } -}; - - -/// Auxiliary template that may be used when implementing LLVMPointsToSetImpl -template -class LLVMPointsToSetImplTemplate : public LLVMPointsToSetImpl { -protected: - PTSetT PTSet; - decltype(PTSet.begin()) it; - size_t _position{0}; - - bool issingleton() const { - // is there any better way how to do it? - auto it = PTSet.begin(); - return it != PTSet.end() && ++it == PTSet.end(); - } - - bool isKnownSingleton() const { return issingleton() - && !hasUnknown() && !hasNull() - && !hasInvalidated(); } - - // find next node that has associated llvm::Value - // (i.e. skip null, unknown, etc.) - virtual void _findNextReal() = 0; - - void initialize_iterator() { - assert(it == PTSet.begin()); - if (!PTSet.empty()) - _findNextReal(); - } - -public: - // NOTE: the child constructor must call initialize_iterator(). - // We cannot call it here since you can't call virtual - // functions in ctor/dtor - LLVMPointsToSetImplTemplate(PTSetT S) - : PTSet(S), it(PTSet.begin()) { - } - - void shift() override { - assert(it != PTSet.end() && "Tried to shift end() iterator"); - ++it; - ++_position; - _findNextReal(); - } - - int position() const override { return _position; } - bool end() const override { return it == PTSet.end(); } - - // NOTE: LLVMPointsToSet will overtake the ownership of this - // object and will delete it on destruction. - LLVMPointsToSet toLLVMPointsToSet() { - return LLVMPointsToSet(this); - } -}; - - -/// Implementation of LLVMPointsToSet that iterates -// over the DG's points-to set -class DGLLVMPointsToSet : public LLVMPointsToSetImplTemplate { - - void _findNextReal() override { - while (it != PTSet.end() && - (!(*it).isValid() || (*it).isInvalidated())) { - ++it; - ++_position; - } - } - -public: - DGLLVMPointsToSet(const PointsToSetT& S) : LLVMPointsToSetImplTemplate(S) { - initialize_iterator(); - } - - /// - // NOTE: this may not be O(1) operation - bool hasUnknown() const override { return PTSet.hasUnknown(); } - bool hasNull() const override { return PTSet.hasNull(); } - bool hasNullWithOffset() const override { return PTSet.hasNullWithOffset(); } - bool hasInvalidated() const override { return PTSet.hasInvalidated(); } - size_t size() const override { return PTSet.size(); } - - LLVMPointer getKnownSingleton() const override { - assert(isKnownSingleton()); - auto ptr = (*(PTSet.begin())); - return LLVMPointer(ptr.target->getUserData(), - ptr.offset); - } - - LLVMPointer get() const override { - assert((it != PTSet.end()) && "Dereferenced end() iterator"); - return LLVMPointer{(*it).target->getUserData(), (*it).offset}; - } -}; - -} // namespace dg - -#endif // _LLVM_DG_POINTS_TO_SET_H_ diff --git a/include/dg/llvm/PointerAnalysis/PointerAnalysis.h b/include/dg/llvm/PointerAnalysis/PointerAnalysis.h deleted file mode 100644 index 2b04a7e20..000000000 --- a/include/dg/llvm/PointerAnalysis/PointerAnalysis.h +++ /dev/null @@ -1,348 +0,0 @@ -#ifndef _LLVM_DG_POINTS_TO_ANALYSIS_H_ -#define _LLVM_DG_POINTS_TO_ANALYSIS_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/PointerAnalysis/PointerGraph.h" -#include "dg/PointerAnalysis/PointerAnalysis.h" -#include "dg/PointerAnalysis/PointerGraphOptimizations.h" -#include "dg/PointerAnalysis/PointerAnalysisFI.h" -#include "dg/PointerAnalysis/PointerAnalysisFS.h" -#include "dg/PointerAnalysis/PointerAnalysisFSInv.h" - -#include "dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h" -#include "dg/llvm/PointerAnalysis/PointerGraph.h" -#include "dg/llvm/PointerAnalysis/LLVMPointsToSet.h" - - -namespace dg { - -using pta::PointerGraph; -using pta::PSNode; -using pta::LLVMPointerGraphBuilder; -using pta::Pointer; - -/// -// Interface for LLVM pointer analysis -class LLVMPointerAnalysis { -protected: - const LLVMPointerAnalysisOptions options{}; - - LLVMPointerAnalysis(const LLVMPointerAnalysisOptions& opts) - : options(opts) {}; -public: - const LLVMPointerAnalysisOptions& getOptions() const { return options; } - - /// - // This method returns true if the pointer analysis - // 1) has any points-to set associated to the value 'val' - // 2) the points-to set is non-empty - virtual bool hasPointsTo(const llvm::Value *val) = 0; - - /// - // Get the points-to information for the given LLVM value. - // The return object has methods begin(), end() that can be used - // for iteration over (llvm::Value *, Offset) pairs of the - // points-to set. Moreover, the object has methods hasUnknown(), - // hasNull(), and hasInvalidated() that reflect whether the points-to set of - // the LLVM value contains unknown element, null, or invalidated. - // If the pointer analysis has no or empty points-to set for 'val' - // (i.e. hasPointsTo() returns false), a points-to set containing - // the only element unknown is returned. - virtual LLVMPointsToSet getLLVMPointsTo(const llvm::Value *val) = 0; - - /// - // This method is the same as getLLVMPointsTo, but it returns - // also the result of hasPointsTo(), so one can check whether the unknown - // pointer in the set is present because hasPointsTo() was false, - // or wether it has been propagated there during the analysis. - virtual std::pair - getLLVMPointsToChecked(const llvm::Value *val) = 0; - - // A convenient wrapper around getLLVMPointsTo - // that takes an instruction and returns a set of - // memory regions that are accessed (read/written) - // by this instruction. It also returns a boolean - // set to true if this information is not known (i.e., - // the points-to set did contain 'unknown' element - // or was empty). For CallInst, it returns memory - // regions that may be accessed via the passed arguments. - std::pair - getAccessedMemory(const llvm::Instruction *I); - - virtual bool run() = 0; - - virtual ~LLVMPointerAnalysis() = default; -}; - -template -class DGLLVMPointerAnalysisImpl : public PTType -{ - LLVMPointerGraphBuilder *builder; - -public: - DGLLVMPointerAnalysisImpl(PointerGraph *PS, LLVMPointerGraphBuilder *b) - : PTType(PS), builder(b) {} - - DGLLVMPointerAnalysisImpl(PointerGraph *PS, LLVMPointerGraphBuilder *b, - const LLVMPointerAnalysisOptions& opts) - : PTType(PS, opts), builder(b) {} - - // build new subgraphs on calls via pointer - bool functionPointerCall(PSNode *callsite, PSNode *called) override { - using namespace pta; - const llvm::Function *F - = llvm::dyn_cast(called->getUserData()); - // with vararg it may happen that we get pointer that - // is not to function, so just bail out here in that case - if (!F) - return false; - - if (F->isDeclaration()) { - if (builder->threads()) { - if (F->getName() == "pthread_create") { - builder->insertPthreadCreateByPtrCall(callsite); - return true; - } else if (F->getName() == "pthread_join") { - builder->insertPthreadJoinByPtrCall(callsite); - return true; - } - } - } - - if (!LLVMPointerGraphBuilder::callIsCompatible(callsite, called)) { - return false; - } - - builder->insertFunctionCall(callsite, called); - - // call the original handler that works on generic graphs - PTType::functionPointerCall(callsite, called); - -#ifndef NDEBUG - // check the graph after rebuilding, but do not check for connectivity, - // because we can call a function that will disconnect the graph - if (!builder->validateSubgraph(true)) { - llvm::errs() << "Pointer Subgraph is broken!\n"; - llvm::errs() << "This happend after building this function called via pointer: " - << F->getName() << "\n"; - abort(); - } -#endif // NDEBUG - - return true; // we changed the graph - } - - bool handleFork(PSNode *forkNode, PSNode *called) override { - using namespace llvm; - using namespace dg::pta; - - assert(called->getType() == PSNodeType::FUNCTION - && "The called value is not a function"); - - PSNodeFork *fork = PSNodeFork::get(forkNode); - builder->addFunctionToFork(called, fork); - -#ifndef NDEBUG - // check the graph after rebuilding, but do not check for connectivity, - // because we can call a function that will disconnect the graph - if (!builder->validateSubgraph(true)) { - const llvm::Function *F - = llvm::cast(called->getUserData()); - llvm::errs() << "Pointer Subgraph is broken!\n"; - llvm::errs() << "This happend after building this function spawned in a thread: " - << F->getName() << "\n"; - abort(); - } -#endif // NDEBUG - - return true; - } - - bool handleJoin(PSNode *joinNode) override { - return builder->matchJoinToRightCreate(joinNode); - } -}; - -class DGLLVMPointerAnalysis : public LLVMPointerAnalysis { - PointerGraph *PS = nullptr; - std::unique_ptr PTA{}; // dg pointer analysis object - std::unique_ptr _builder; - - LLVMPointerAnalysisOptions createOptions(const char *entry_func, - uint64_t field_sensitivity, - bool threads = false) - { - LLVMPointerAnalysisOptions opts; - opts.threads = threads; - opts.setFieldSensitivity(field_sensitivity); - opts.setEntryFunction(entry_func); - return opts; - } - - const PointsToSetT& getUnknownPTSet() const { - static const PointsToSetT _unknownPTSet - = PointsToSetT({Pointer{pta::UNKNOWN_MEMORY, 0}}); - return _unknownPTSet; - } - -public: - - DGLLVMPointerAnalysis(const llvm::Module *m, - const char *entry_func = "main", - uint64_t field_sensitivity = Offset::UNKNOWN, - bool threads = false) - : DGLLVMPointerAnalysis(m, createOptions(entry_func, field_sensitivity, threads)) {} - - DGLLVMPointerAnalysis(const llvm::Module *m, const LLVMPointerAnalysisOptions opts) - : LLVMPointerAnalysis(opts), _builder(new LLVMPointerGraphBuilder(m, opts)) {} - - /// - // Get the node from pointer analysis that holds the points-to set. - // See: getLLVMPointsTo() - PSNode *getPointsToNode(const llvm::Value *val) const { - return _builder->getPointsToNode(val); - } - - pta::PointerAnalysis *getPTA() { return PTA.get(); } - const pta::PointerAnalysis *getPTA() const { return PTA.get(); } - - bool threads() const { return _builder->threads(); } - - bool hasPointsTo(const llvm::Value *val) override { - if (auto node = getPointsToNode(val)) { - return !node->pointsTo.empty(); - } - return false; - } - - /// - // Get the points-to information for the given LLVM value. - // The return object has methods begin(), end() that can be used - // for iteration over (llvm::Value *, Offset) pairs of the - // points-to set. Moreover, the object has methods hasUnknown() - // and hasNull() that reflect whether the points-to set of the - // LLVM value contains unknown element of null. - LLVMPointsToSet getLLVMPointsTo(const llvm::Value *val) override { - DGLLVMPointsToSet *pts; - if (auto node = getPointsToNode(val)) { - if (node->pointsTo.empty()) { - pts = new DGLLVMPointsToSet(getUnknownPTSet()); - } else { - pts = new DGLLVMPointsToSet(node->pointsTo); - } - } else { - pts = new DGLLVMPointsToSet(getUnknownPTSet()); - } - return pts->toLLVMPointsToSet(); - } - - /// - // This method is the same as getLLVMPointsTo, but it returns - // also the information whether the node of pointer analysis exists - // (opposed to the getLLVMPointsTo, which returns a set with - // unknown element when the node does not exists) - std::pair - getLLVMPointsToChecked(const llvm::Value *val) override { - DGLLVMPointsToSet *pts; - if (auto node = getPointsToNode(val)) { - if (node->pointsTo.empty()) { - pts = new DGLLVMPointsToSet(getUnknownPTSet()); - return {false, pts->toLLVMPointsToSet()}; - } else { - pts = new DGLLVMPointsToSet(node->pointsTo); - return {true, pts->toLLVMPointsToSet()}; - } - } else { - pts = new DGLLVMPointsToSet(getUnknownPTSet()); - return {false, pts->toLLVMPointsToSet()}; - } - } - - const std::vector>& getNodes() - { - return PS->getNodes(); - } - - std::vector getFunctionNodes(const llvm::Function *F) const { - return _builder->getFunctionNodes(F); - } - - PointerGraph *getPS() { return PS; } - const PointerGraph *getPS() const { return PS; } - - LLVMPointerGraphBuilder *getBuilder() { return _builder.get(); } - const LLVMPointerGraphBuilder *getBuilder() const { return _builder.get(); } - - void buildSubgraph() { - // run the analysis itself - assert(_builder && "Incorrectly constructed PTA, missing builder"); - - PS = _builder->buildLLVMPointerGraph(); - if (!PS) { - llvm::errs() << "Pointer Subgraph was not built, aborting\n"; - abort(); - } - - /* - pta::PointerGraphOptimizer optimizer(PS); - optimizer.run(); - - if (optimizer.getNumOfRemovedNodes() > 0) - _builder->composeMapping(std::move(optimizer.getMapping())); - - llvm::errs() << "PS optimization removed " << optimizer.getNumOfRemovedNodes() << " nodes\n"; - */ - } - - void initialize() { - if (options.isFSInv()) - _builder->setInvalidateNodesFlag(true); - - buildSubgraph(); - - if (options.isFS()) { - // FIXME: make a interface with run() method - PTA.reset(new DGLLVMPointerAnalysisImpl( - PS, _builder.get(), options)); - } else if (options.isFI()) { - PTA.reset(new DGLLVMPointerAnalysisImpl( - PS, _builder.get(), options)); - } else if (options.isFSInv()) { - PTA.reset(new DGLLVMPointerAnalysisImpl( - PS, _builder.get(), options)); - } else { - assert(0 && "Wrong pointer analysis"); - abort(); - } - } - - bool run() override { - if (!PTA) { - initialize(); - } - return PTA->run(); - } -}; - -// an auxiliary function -inline std::vector -getCalledFunctions(const llvm::Value *calledValue, LLVMPointerAnalysis *PTA) { - std::vector functions; - for (const auto& llvmptr : PTA->getLLVMPointsTo(calledValue)) { - if (const auto F = llvm::dyn_cast(llvmptr.value)) { - functions.push_back(F); - } - } - return functions; -} - -} // namespace dg - -#endif // _LLVM_DG_POINTS_TO_ANALYSIS_H_ diff --git a/include/dg/llvm/PointerAnalysis/PointerGraph.h b/include/dg/llvm/PointerAnalysis/PointerGraph.h deleted file mode 100644 index a9764ab9a..000000000 --- a/include/dg/llvm/PointerAnalysis/PointerGraph.h +++ /dev/null @@ -1,453 +0,0 @@ -#ifndef _LLVM_DG_POINTER_SUBGRAPH_H_ -#define _LLVM_DG_POINTER_SUBGRAPH_H_ - -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h" - -#include "dg/PointerAnalysis/PointerGraph.h" -#include "dg/PointerAnalysis/PointsToMapping.h" -#include "dg/PointerAnalysis/Pointer.h" - - -namespace dg { -namespace pta { - -class LLVMPointerGraphBuilder -{ - PointerGraph PS{}; - // mapping from llvm values to PSNodes that contain - // the points-to information - PointsToMapping mapping; - - const llvm::Module *M; - LLVMPointerAnalysisOptions _options; - - // flag that says whether we are building normally, - // or the analysis is already running and we are building - // some new parts of already built graph. - // This is important with function pointer calls - bool ad_hoc_building = false; - // flag that determines whether invalidate nodes - // should be created - bool invalidate_nodes = false; - - bool threads_ = false; - - class PSNodesSeq { - using NodesT = std::vector; - NodesT _nodes; - // representant that holds the final points-to set after - // generated by this sequence of instructions - PSNode *_repr{nullptr}; - - public: - PSNodesSeq() = default; - PSNodesSeq(PSNode *n) { _nodes.push_back(n); } - PSNodesSeq(const std::initializer_list& l) { - for (auto n : l) - _nodes.push_back(n); - } - - void setRepresentant(PSNode *r) { _repr = r; } - PSNode *getRepresentant() { return _repr ? _repr : _nodes.back(); } - const PSNode *getRepresentant() const { return _repr ? _repr : _nodes.back(); } - - PSNode *getSingleNode() { assert(_nodes.size() == 1); return _nodes.front(); } - const PSNode *getSingleNode() const { assert(_nodes.size() == 1); return _nodes.front(); } - - void append(PSNode *n) { _nodes.push_back(n); } - bool empty() const { return _nodes.empty(); } - - PSNode *getFirst() {assert(!_nodes.empty()); return _nodes.front(); } - PSNode *getLast() { assert(!_nodes.empty()); return _nodes.back(); } - - NodesT::iterator begin() { return _nodes.begin(); } - NodesT::const_iterator begin() const { return _nodes.begin(); } - NodesT::iterator end() { return _nodes.end(); } - NodesT::const_iterator end() const { return _nodes.end(); } - }; - - class PSNodesBlock { - using NodesT = std::vector; - NodesT _nodes; - - public: - PSNodesBlock() = default; - PSNodesBlock(PSNodesSeq *s) { append(s); } - void append(PSNodesSeq *s) { _nodes.push_back(s); } - bool empty() const { return _nodes.empty(); } - - PSNodesSeq& getFirst() {assert(!empty()); return *_nodes.front(); } - PSNodesSeq& getLast() { assert(!empty()); return *_nodes.back(); } - PSNode *getFirstNode() {assert(!empty()); return _nodes.front()->getFirst(); } - PSNode *getLastNode() { assert(!empty()); return _nodes.back()->getLast(); } - - NodesT::iterator begin() { return _nodes.begin(); } - NodesT::const_iterator begin() const { return _nodes.begin(); } - NodesT::iterator end() { return _nodes.end(); } - NodesT::const_iterator end() const { return _nodes.end(); } - }; - - struct FuncGraph { - // reachable LLVM block (those block for which we built the instructions) - std::map llvmBlocks{}; - bool has_structure{false}; - - FuncGraph() = default; - FuncGraph(const FuncGraph&) = delete; - - void blockAddSuccessors(std::set& found_blocks, - LLVMPointerGraphBuilder::PSNodesBlock& blk, - const llvm::BasicBlock& block); - }; - - // helper function that add CFG edges between instructions - void PSNodesSequenceAddSuccessors(PSNodesSeq& seq) { - if (seq.empty()) - return; - - PSNode *last = nullptr; - for (auto nd : seq) { - if (last) - last->addSuccessor(nd); - - last = nd; - } - } - - void PSNodesBlockAddSuccessors(PSNodesBlock& blk, bool withSeqEdges = false) { - if (blk.empty()) - return; - - PSNodesSeq *last = nullptr; - for (auto seq : blk) { - if (withSeqEdges) - PSNodesSequenceAddSuccessors(*seq); - - if (last) - last->getLast()->addSuccessor(seq->getFirst()); - - last = seq; - } - } - - std::unordered_map _funcInfo; - - // build pointer state subgraph for given graph - // \return root node of the graph - PointerSubgraph& buildFunction(const llvm::Function& F); - PSNodesSeq& buildInstruction(const llvm::Instruction&); - - PSNodesBlock - buildPointerGraphBlock(const llvm::BasicBlock& block, - PointerSubgraph *parent); - - void buildArguments(const llvm::Function& F, - PointerSubgraph *parent); - PSNodesBlock buildArgumentsStructure(const llvm::Function& F); - void buildGlobals(); - - // add edges that are derived from CFG to the subgraph - void addProgramStructure(); - void addProgramStructure(const llvm::Function *F, PointerSubgraph& subg); - void blockAddCalls(const llvm::BasicBlock& block); - - void addCFGEdges(const llvm::Function *F, - LLVMPointerGraphBuilder::FuncGraph& finfo, - PSNode *lastNode); - - PSNode *connectArguments(const llvm::Function *F, - PSNodesBlock& argsBlk, PointerSubgraph& subg); - - // map of all nodes we created - use to look up operands - std::unordered_map nodes_map; - // map of all built subgraphs - the value type is a pair (root, return) - std::unordered_map subgraphs_map; - - std::vector forkNodes; - std::vector joinNodes; - -public: - const PointerGraph *getPS() const { return &PS; } - - inline bool threads() const { return threads_; } - - LLVMPointerGraphBuilder(const llvm::Module *m, const LLVMPointerAnalysisOptions& opts) - : M(m), _options(opts), threads_(opts.threads) {} - - PointerGraph *buildLLVMPointerGraph(); - - bool validateSubgraph(bool no_connectivity = false) const; - - void setAdHocBuilding(bool adHoc) { ad_hoc_building = adHoc; } - - PSNodesSeq& - createFuncptrCall(const llvm::CallInst *CInst, - const llvm::Function *F); - - static bool callIsCompatible(PSNode *call, PSNode *func); - - // Insert a call of a function into an already existing graph. - // The call will be inserted betwee the callsite and - // the return from the call nodes. - void insertFunctionCall(PSNode *callsite, PSNode *called); - void insertPthreadCreateByPtrCall(PSNode *callsite); - void insertPthreadJoinByPtrCall(PSNode *callsite); - - PSNodeFork *createForkNode(const llvm::CallInst *CInst, PSNode *); - PSNodeJoin *createJoinNode(const llvm::CallInst *CInst, PSNode *); - PSNodesSeq createPthreadCreate(const llvm::CallInst *CInst); - PSNodesSeq createPthreadJoin(const llvm::CallInst *CInst); - PSNodesSeq createPthreadExit(const llvm::CallInst *CInst); - - bool addFunctionToFork(PSNode * function, - PSNodeFork *forkNode); - bool addFunctionToJoin(PSNode *function, - PSNodeJoin * joinNode); - - bool matchJoinToRightCreate(PSNode *pthreadJoinCall); - // let the user get the nodes map, so that we can - // map the points-to informatio back to LLVM nodes - const std::unordered_map& - getNodesMap() const { return nodes_map; } - - std::vector getFunctionNodes(const llvm::Function *F) const; - - // this is the same as the getNode, but it creates ConstantExpr - PSNode *getPointsToNode(const llvm::Value *val) { - PSNode *n = getPointsToNodeOrNull(val); - if (!n) - n = getConstant(val); - - return n; - } - - std::vector - getPointsToFunctions(const llvm::Value *calledValue); - - std::vector& getJoins() { return joinNodes; } - std::vector& getForks() { return forkNodes; } - const std::vector& getJoins() const { return joinNodes; } - const std::vector& getForks() const { return forkNodes; } - - PSNodeJoin * findJoin(const llvm::CallInst * callInst) const; - void setInvalidateNodesFlag(bool value) - { - assert(PS.getEntry() == nullptr && - "This function must be called before building PS"); - this->invalidate_nodes = value; - } - - void composeMapping(PointsToMapping&& rhs) { - mapping.compose(std::move(rhs)); - } - - PointerSubgraph *getSubgraph(const llvm::Function *); - -private: - - // create subgraph of function @F (the nodes) - // and call+return nodes to/from it. This function - // won't add the CFG edges if not 'ad_hoc_building' - // is set to true - PSNodesSeq& - createCallToFunction(const llvm::CallInst *, const llvm::Function *); - - PSNode *getPointsToNodeOrNull(const llvm::Value *val) { - // if we have a mapping for this node (e.g. the original - // node was optimized away and replaced by mapping), - // return it - if (auto mp = mapping.get(val)) - return mp; - else if (auto nds = getNodes(val)) { - // otherwise get the representant of the built nodes - return nds->getRepresentant(); - } - - // not built! - return nullptr; - } - - // get the built nodes for this value or null - PSNodesSeq *getNodes(const llvm::Value *val) { - auto it = nodes_map.find(val); - if (it == nodes_map.end()) - return nullptr; - - // the node corresponding to the real llvm value - // is always the last - return &it->second; - } - - PSNodesSeq& addNode(const llvm::Value *val, PSNode *node) { - assert(nodes_map.find(val) == nodes_map.end()); - auto it = nodes_map.emplace(val, node); - node->setUserData(const_cast(val)); - - return it.first->second; - } - - PSNodesSeq& addNode(const llvm::Value *val, PSNodesSeq seq) { - assert(nodes_map.find(val) == nodes_map.end()); - seq.getRepresentant()->setUserData(const_cast(val)); - auto it = nodes_map.emplace(val, std::move(seq)); - - return it.first->second; - } - - bool isRelevantInstruction(const llvm::Instruction& Inst); - - PSNodesSeq& createAlloc(const llvm::Instruction *Inst); - PSNode *createDynamicAlloc(const llvm::CallInst *CInst, - AllocationFunction type); - PSNodesSeq& createStore(const llvm::Instruction *Inst); - PSNodesSeq& createLoad(const llvm::Instruction *Inst); - PSNodesSeq& createGEP(const llvm::Instruction *Inst); - PSNodesSeq& createSelect(const llvm::Instruction *Inst); - PSNodesSeq& createPHI(const llvm::Instruction *Inst); - PSNodesSeq& createCast(const llvm::Instruction *Inst); - PSNodesSeq& createReturn(const llvm::Instruction *Inst); - PSNodesSeq& createPtrToInt(const llvm::Instruction *Inst); - PSNodesSeq& createIntToPtr(const llvm::Instruction *Inst); - PSNodesSeq& createAsm(const llvm::Instruction *Inst); - PSNodesSeq& createInsertElement(const llvm::Instruction *Inst); - PSNodesSeq& createExtractElement(const llvm::Instruction *Inst); - PSNodesSeq& createAtomicRMW(const llvm::Instruction *Inst); - PSNodesSeq& createConstantExpr(const llvm::ConstantExpr *CE); - - PSNode* createInternalLoad(const llvm::Instruction *Inst); - PSNodesSeq& createIrrelevantInst(const llvm::Value *, - bool build_uses = false); - PSNodesSeq& createArgument(const llvm::Argument *); - void createIrrelevantUses(const llvm::Value *val); - - PSNodesSeq& createAdd(const llvm::Instruction *Inst); - PSNodesSeq& createArithmetic(const llvm::Instruction *Inst); - PSNodesSeq& createUnknown(const llvm::Value *val); - PSNode *createFree(const llvm::Instruction *Inst); - PSNode *createLifetimeEnd(const llvm::Instruction *Inst); - - PSNode *getOperand(const llvm::Value *val); - PSNode *tryGetOperand(const llvm::Value *val); - PSNode *getConstant(const llvm::Value *val); - Pointer handleConstantGep(const llvm::GetElementPtrInst *GEP); - Pointer handleConstantBitCast(const llvm::BitCastInst *BC); - Pointer handleConstantPtrToInt(const llvm::PtrToIntInst *P2I); - Pointer handleConstantIntToPtr(const llvm::IntToPtrInst *I2P); - Pointer handleConstantAdd(const llvm::Instruction *Inst); - Pointer handleConstantArithmetic(const llvm::Instruction *Inst); - Pointer getConstantExprPointer(const llvm::ConstantExpr *CE); - - void checkMemSet(const llvm::Instruction *Inst); - void addPHIOperands(PSNode *node, const llvm::PHINode *PHI); - void addPHIOperands(const llvm::Function& F); - void addArgumentOperands(const llvm::Function *F, PSNode *arg, int idx); - void addArgumentOperands(const llvm::CallInst *CI, PSNode *arg, int idx); - void addArgumentOperands(const llvm::CallInst &CI, PSNode &node); - void addArgumentsOperands(const llvm::Function *F, - const llvm::CallInst *CI = nullptr, - int index = 0); - void addVariadicArgumentOperands(const llvm::Function *F, PSNode *arg); - void addVariadicArgumentOperands(const llvm::Function *F, - const llvm::CallInst *CI, - PSNode *arg); - - void addReturnNodesOperands(const llvm::Function *F, - PointerSubgraph& subg, - PSNode *callNode = nullptr); - - void addReturnNodeOperand(PSNode *callNode, PSNode *op); - void addReturnNodeOperand(const llvm::Function *F, PSNode *op); - void addInterproceduralOperands(const llvm::Function *F, - PointerSubgraph& subg, - const llvm::CallInst *CI = nullptr, - PSNode *callNode = nullptr); - void addInterproceduralPthreadOperands(const llvm::Function *F, - const llvm::CallInst *CI = nullptr); - - PSNodesSeq& createExtract(const llvm::Instruction *Inst); - PSNodesSeq& createCall(const llvm::Instruction *Inst); - PSNodesSeq& createFunctionCall(const llvm::CallInst *, const llvm::Function *); - PSNodesSeq createUndefFunctionCall(const llvm::CallInst *, const llvm::Function *); - PSNodesSeq& createFuncptrCall(const llvm::CallInst *, const llvm::Value *); - - PointerSubgraph& createOrGetSubgraph(const llvm::Function *); - PointerSubgraph& getAndConnectSubgraph(const llvm::Function *F, - const llvm::CallInst *CInst, - PSNode *callNode); - - void handleGlobalVariableInitializer(const llvm::Constant *C, - PSNodeAlloc *node, - uint64_t offset = 0); - - PSNode *createMemTransfer(const llvm::IntrinsicInst *Inst); - PSNodesSeq createMemSet(const llvm::Instruction *); - PSNodesSeq createDynamicMemAlloc(const llvm::CallInst *CInst, - AllocationFunction type); - PSNodesSeq createRealloc(const llvm::CallInst *CInst); - PSNode* createUnknownCall(); - PSNodesSeq createIntrinsic(const llvm::Instruction *Inst); - PSNodesSeq createVarArg(const llvm::IntrinsicInst *Inst); -}; - -/// -------------------------------------------------------- -// Helper functions -/// -------------------------------------------------------- -inline bool isRelevantIntrinsic(const llvm::Function *func, bool invalidate_nodes) -{ - using namespace llvm; - - switch (func->getIntrinsicID()) { - case Intrinsic::memmove: - case Intrinsic::memcpy: - case Intrinsic::vastart: - case Intrinsic::stacksave: - case Intrinsic::stackrestore: - return true; - case Intrinsic::lifetime_end: - return invalidate_nodes; - // case Intrinsic::memset: - default: - return false; - } -} - -inline bool isInvalid(const llvm::Value *val, bool invalidate_nodes) -{ - using namespace llvm; - - if (!isa(val)) { - if (!isa(val) && !isa(val)) - return true; - } else { - if (isa(val) || isa(val) - || isa(val) || isa(val) - || isa(val)) - return true; - - const CallInst *CI = dyn_cast(val); - if (CI) { - const Function *F = CI->getCalledFunction(); - if (F && F->isIntrinsic() && !isRelevantIntrinsic(F, invalidate_nodes)) - return true; - } - } - - return false; -} - -} // namespace pta -} // namespace dg - -#endif diff --git a/include/dg/llvm/PointerAnalysis/PointerGraphValidator.h b/include/dg/llvm/PointerAnalysis/PointerGraphValidator.h deleted file mode 100644 index 43b1346cd..000000000 --- a/include/dg/llvm/PointerAnalysis/PointerGraphValidator.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef LLVM_DG_POINTER_SUBGRAPH_VALIDATOR_H_ -#define LLVM_DG_POINTER_SUBGRAPH_VALIDATOR_H_ - -#include "dg/PointerAnalysis/PointerGraph.h" -#include "dg/PointerAnalysis/PointerGraphValidator.h" - -namespace dg { -namespace pta { - -/** - * Take PointerGraph instance and check whether it is not broken - */ -class LLVMPointerGraphValidator : public PointerGraphValidator { - bool reportInvalOperands(const PSNode *n, const std::string& user_err) override; - -public: - LLVMPointerGraphValidator(const PointerGraph *ps, - bool no_connectivity = false) - : PointerGraphValidator(ps, no_connectivity) {} - - ~LLVMPointerGraphValidator() = default; -}; - -} // namespace pta -} // namespace dg - - - -#endif diff --git a/include/dg/llvm/PointerAnalysis/SVFPointerAnalysis.h b/include/dg/llvm/PointerAnalysis/SVFPointerAnalysis.h deleted file mode 100644 index d6991f15e..000000000 --- a/include/dg/llvm/PointerAnalysis/SVFPointerAnalysis.h +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef DG_SVF_POINTER_ANALYSIS_H_ -#define DG_SVF_POINTER_ANALYSIS_H_ - -#ifndef HAVE_SVF -#error "Do not have SVF" -#endif - -#if (__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-parameter" -#pragma clang diagnostic ignored "-Wreorder" -#pragma clang diagnostic ignored "-Wignored-qualifiers" -#pragma clang diagnostic ignored "-Woverloaded-virtual" -#pragma clang diagnostic ignored "-Wunused-variable" -#else -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Wreorder" -#pragma GCC diagnostic ignored "-Wignored-qualifiers" -#pragma GCC diagnostic ignored "-Woverloaded-virtual" -#pragma GCC diagnostic ignored "-Wunused-variable" -#pragma GCC diagnostic ignored "-Wunused-but-set-variable" -#endif - -#include -#include -#include - -#include "WPA/Andersen.h" // Andersen analysis from SVF -#include "SVF-FE/LLVMModule.h" // LLVMModuleSet -#include "SVF-FE/PAGBuilder.h" // PAGBuilder - -#if (__clang__) -#pragma clang diagnostic pop -#else -#pragma GCC diagnostic pop -#endif - -#include "dg/PointerAnalysis/Pointer.h" - -#include "dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h" -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/PointerAnalysis/LLVMPointsToSet.h" - -#include "dg/util/debug.h" - -namespace dg { - -using pta::Pointer; - -using SVF::PAG; -using SVF::LLVMModuleSet; -using SVF::PointsTo; - -/// Implementation of LLVMPointsToSet that iterates -// over the DG's points-to set -class SvfLLVMPointsToSet : public LLVMPointsToSetImplTemplate { - PAG *_pag; - size_t _position{0}; - - llvm::Value *_getValue(unsigned id) const { - auto pagnode = _pag->getPAGNode(id); - if (pagnode->hasValue()) { - return const_cast(pagnode->getValue()); - } else { - // for debuggin right now - llvm::errs() << "[SVF] No value in PAG NODE\n"; - llvm::errs() << *pagnode << "\n"; - return nullptr; - } - } - - void _findNextReal() override { - while ((it != PTSet.end())) { - if (_pag->getPAGNode(*it)->hasValue()) { - break; - } - //else { - // llvm::errs() << "no val" << *_pag->getPAGNode(*it) << "\n"; - //} - ++it; - ++_position; - } - } - -public: - SvfLLVMPointsToSet(PointsTo& S, PAG *pag) - : LLVMPointsToSetImplTemplate(std::move(S)), _pag(pag) { - initialize_iterator(); - } - - bool hasUnknown() const override { - return PTSet.test(_pag->getBlackHoleNode()); - } - - bool hasNull() const override { - return PTSet.test(_pag->getNullPtr()); - } - - bool hasNullWithOffset() const override { - // we are field-insensitive now... - return hasNull(); - } - - bool hasInvalidated() const override { return false; } - size_t size() const override { return PTSet.count(); } - - LLVMPointer getKnownSingleton() const override { - assert(isKnownSingleton()); - return LLVMPointer(_getValue(*PTSet.begin()), Offset::UNKNOWN); - } - - LLVMPointer get() const override { - assert(it != PTSet.end() && "Dereferenced end() iterator"); - return LLVMPointer{_getValue(*it), Offset::UNKNOWN}; - } -}; - - -/// -// Integration of pointer analysis from SVF -class SVFPointerAnalysis : public LLVMPointerAnalysis { - const llvm::Module *_module{nullptr}; - SVF::SVFModule *_svfModule{nullptr}; - std::unique_ptr _pta{}; - - PointsTo& getUnknownPTSet() const { - static PointsTo _unknownPTSet; - if (_unknownPTSet.empty()) - _unknownPTSet.set(_pta->getPAG()->getBlackHoleNode()); - return _unknownPTSet; - } - - LLVMPointsToSet mapSVFPointsTo(PointsTo& S, PAG *pag) { - SvfLLVMPointsToSet *pts; - if (S.empty()) { - pts = new SvfLLVMPointsToSet(getUnknownPTSet(), pag); - } else { - pts = new SvfLLVMPointsToSet(S, pag); - } - return pts->toLLVMPointsToSet(); - } - - -public: - SVFPointerAnalysis(const llvm::Module *M, - const LLVMPointerAnalysisOptions& opts) - : LLVMPointerAnalysis(opts), _module(M) {} - - ~SVFPointerAnalysis() { - // _svfModule overtook the ovenership of llvm::Module, - // we must re-take it to avoid double free - LLVMModuleSet::releaseLLVMModuleSet(); - } - - bool hasPointsTo(const llvm::Value *val) override { - PAG* pag = _pta->getPAG(); - auto pts = _pta->getPts(pag->getValueNode(val)); - return !pts.empty(); - } - - /// - // Get the points-to information for the given LLVM value. - // The return object has methods begin(), end() that can be used - // for iteration over (llvm::Value *, Offset) pairs of the - // points-to set. Moreover, the object has methods hasUnknown() - // and hasNull() that reflect whether the points-to set of the - // LLVM value contains unknown element of null. - LLVMPointsToSet getLLVMPointsTo(const llvm::Value *val) override { - PAG* pag = _pta->getPAG(); - auto pts = _pta->getPts(pag->getValueNode(val)); - return mapSVFPointsTo(pts, pag); - } - - /// - // This method is the same as getLLVMPointsTo, but it returns - // also the information whether the node of pointer analysis exists - // (opposed to the getLLVMPointsTo, which returns a set with - // unknown element when the node does not exists) - std::pair - getLLVMPointsToChecked(const llvm::Value *val) override { - PAG* pag = _pta->getPAG(); - auto pts = _pta->getPts(pag->getValueNode(val)); - return {!pts.empty(), mapSVFPointsTo(pts, pag)}; - } - - bool run() override { - using namespace SVF; - - DBG_SECTION_BEGIN(pta, "Running SVF pointer analysis (Andersen)"); - - auto moduleset = LLVMModuleSet::getLLVMModuleSet(); - _svfModule = moduleset->buildSVFModule(*const_cast(_module)); - assert(_svfModule && "Failed building SVF module"); - - PAGBuilder builder; - PAG* pag = builder.build(_svfModule); - - _pta.reset(new Andersen(pag)); - _pta->disablePrintStat(); - _pta->analyze(); - - DBG_SECTION_END(pta, "Done running SVF pointer analysis (Andersen)"); - return true; - } -}; - -} // namespace dg - -#endif // DG_SVF_POINTERS_TO_ANALYSIS_H_ diff --git a/include/dg/llvm/ReadWriteGraph/LLVMReadWriteGraphBuilder.h b/include/dg/llvm/ReadWriteGraph/LLVMReadWriteGraphBuilder.h deleted file mode 100644 index 38dbd98c3..000000000 --- a/include/dg/llvm/ReadWriteGraph/LLVMReadWriteGraphBuilder.h +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef LLVM_DG_RWG_BUILDER_H -#define LLVM_DG_RWG_BUILDER_H - -#include -#include -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/ReadWriteGraph/ReadWriteGraph.h" -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h" -#include "dg/llvm/CallGraph/CallGraph.h" - -#include "dg/llvm/GraphBuilder.h" - -#ifndef NDEBUG -#include "dg/util/debug.h" -#endif // NDEBUG - - -namespace dg { -namespace dda { - -class LLVMReadWriteGraphBuilder : public GraphBuilder { - const LLVMDataDependenceAnalysisOptions& _options; - // points-to information - dg::LLVMPointerAnalysis *PTA; - // even the data-flow analysis needs uses to have the mapping of llvm values - bool buildUses{true}; - // optimization for reaching-definitions analysis - // TODO: do not do this while building graph, but let the analysis - // modify the graph itself (or forget it some other way as we'll - // have the interprocedural graph) - // bool forgetLocalsAtReturn{false}; - - ReadWriteGraph graph; - - //RWNode& getOperand(const llvm::Value *) override; - NodesSeq createNode(const llvm::Value *) override; - RWBBlock& createBBlock(const llvm::BasicBlock *, RWSubgraph& subg) override { - return subg.createBBlock(); - } - - RWSubgraph& createSubgraph(const llvm::Function *F) override { - auto& subg = graph.createSubgraph(); - subg.setName(F->getName().str()); - return subg; - } - - /* - std::map threadCreateCalls; - std::map threadJoinCalls; - - // mapping of call nodes to called subgraphs - std::map, std::set> calls; - */ - - RWNode& create(RWNodeType t) { return graph.create(t); } - -public: - LLVMReadWriteGraphBuilder(const llvm::Module *m, - dg::LLVMPointerAnalysis *p, - const LLVMDataDependenceAnalysisOptions& opts) - : GraphBuilder(m), _options(opts), PTA(p) {} - - ReadWriteGraph&& build() { - // FIXME: this is a bit of a hack - if (!PTA->getOptions().isSVF()) { - auto dgpta = static_cast(PTA); - llvmdg::CallGraph CG(dgpta->getPTA()->getPG()->getCallGraph()); - buildFromLLVM(&CG); - } else { - buildFromLLVM(); - } - - auto *entry = getModule()->getFunction(_options.entryFunction); - assert(entry && "Did not find the entry function"); - graph.setEntry(getSubgraph(entry)); - - return std::move(graph); - } - - RWNode *getOperand(const llvm::Value *val); - - std::vector mapPointers(const llvm::Value *where, - const llvm::Value *val, - Offset size); - - RWNode *createStore(const llvm::Instruction *Inst); - RWNode *createLoad(const llvm::Instruction *Inst); - RWNode *createAtomicRMW(const llvm::Instruction *Inst); - RWNode *createAlloc(const llvm::Instruction *Inst); - RWNode *createDynAlloc(const llvm::Instruction *Inst, AllocationFunction type); - RWNode *createRealloc(const llvm::Instruction *Inst); - RWNode *createReturn(const llvm::Instruction *Inst); - - void addReallocUses(const llvm::Instruction *Inst, - RWNode& node, uint64_t size); - - RWNode *funcFromModel(const FunctionModel *model, const llvm::CallInst *); - - NodesSeq createCall(const llvm::Instruction *Inst); - - RWNode *createCallToUndefinedFunction(const llvm::Function *function, - const llvm::CallInst *CInst); - - NodesSeq - createCallToFunctions(const std::vector &functions, - const llvm::CallInst *CInst); - - RWNode *createPthreadCreateCalls(const llvm::CallInst *CInst); - RWNode *createPthreadJoinCall(const llvm::CallInst *CInst); - RWNode *createPthreadExitCall(const llvm::CallInst *CInst); - - RWNode *createIntrinsicCall(const llvm::CallInst *CInst); - RWNode *createUnknownCall(const llvm::CallInst *CInst); - - //void matchForksAndJoins(); -}; - -struct ValInfo { - const llvm::Value *v; - ValInfo(const llvm::Value *val) : v(val) {} -}; - -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, const ValInfo& vi); - - -} // namespace dda -} // namespace dg - -#endif diff --git a/include/dg/llvm/SystemDependenceGraph/AnnotationWriter.h b/include/dg/llvm/SystemDependenceGraph/AnnotationWriter.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/include/dg/llvm/SystemDependenceGraph/SDG2Dot.h b/include/dg/llvm/SystemDependenceGraph/SDG2Dot.h deleted file mode 100644 index 3018519cb..000000000 --- a/include/dg/llvm/SystemDependenceGraph/SDG2Dot.h +++ /dev/null @@ -1,279 +0,0 @@ -#ifndef DG_LLVM_SDG2DOT_H_ -#define DG_LLVM_SDG2DOT_H_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include "llvm/IR/Instructions.h" - -#if ((LLVM_VERSION_MAJOR == 3) && (LLVM_VERSION_MINOR <= 4)) -#include "llvm/DebugInfo.h" //DIScope -#else -#include "llvm/IR/DebugInfo.h" //DIScope -#endif -SILENCE_LLVM_WARNINGS_POP - -#include -#include -#include -#include - -#include "dg/llvm/SystemDependenceGraph/SystemDependenceGraph.h" -#include "dg/SystemDependenceGraph/DGNodeCall.h" - -namespace dg { -namespace llvmdg { - -/* -FIXME: move to one file... -static std::ostream& operator<<(std::ostream& os, const analysis::Offset& off) -{ - if (off.offset == Offset::UNKNOWN) - os << "UNKNOWN"; - else - os << off.offset; - - return os; -} -*/ - -namespace { -static inline std::ostream& printLLVMVal(std::ostream& os, const llvm::Value *val) { - if (!val) { - os << "(null)"; - return os; - } - - std::ostringstream ostr; - llvm::raw_os_ostream ro(ostr); - - if (llvm::isa(val)) { - ro << "FUN " << val->getName(); - } else if (auto B = llvm::dyn_cast(val)) { - ro << B->getParent()->getName() << "::"; - ro << "label " << val->getName(); - } else if (auto I = llvm::dyn_cast(val)) { - const auto B = I->getParent(); - if (B) { - ro << B->getParent()->getName() << "::"; - } else { - ro << "::"; - } - ro << *val; - } else { - ro << *val; - } - - ro.flush(); - - // break the string if it is too long - std::string str = ostr.str(); - if (str.length() > 50) { - str.resize(40); - } - - // escape the " - size_t pos = 0; - while ((pos = str.find('"', pos)) != std::string::npos) { - str.replace(pos, 1, "\\\""); - // we replaced one char with two, so we must shift after the new " - pos += 2; - } - - os << str; - - return os; -} -} // anonymous namespace - -static std::ostream& operator<<(std::ostream& os, const sdg::DGElement& nd) { - os << "elem" << nd.getDG().getID() << "_" << nd.getID(); - return os; -} - - -class SDG2Dot { - SystemDependenceGraph* _llvmsdg; - - // keep track of dumped nodes for checking that we dumped all - mutable std::set dumpedNodes; - - void dumpNode(std::ostream& out, sdg::DGNode& nd, - const llvm::Value *v = nullptr, - const char *descr = nullptr) const { - assert(sdg::DGNode::get(&nd) && "Wrong type"); - - auto& dg = nd.getDG(); - out << " " << nd << - " [label=\"[" << dg.getID() << "." << nd.getID() << "] "; - if (v) { - // this node is associated to this value - printLLVMVal(out, v); - } else { - printLLVMVal(out, _llvmsdg->getValue(&nd)); - } - if (descr) { - out << " " << descr; - } - out << "\"]\n"; - } - - void dumpParams(std::ostream& out, - sdg::DGParameters& params, - const std::string& name) const { - /// input parameters - out << " subgraph cluster_params_in_" << ¶ms << " {\n"; - out << " label=\"" << name << " (input)\"\n"; - for (auto& param : params) { - auto& nd = param.getInputArgument(); - dumpedNodes.insert(&nd); - dumpNode(out, nd, _llvmsdg->getValue(¶m)); - } - out << " }\n"; - - /// output parameters - out << " subgraph cluster_params_out_" << ¶ms << " {\n"; - out << " label=\"" << name << " (output)\"\n"; - for (auto& param : params) { - auto& nd = param.getOutputArgument(); - dumpedNodes.insert(&nd); - dumpNode(out, nd, _llvmsdg->getValue(¶m)); - } - if (auto *noret = params.getNoReturn()) { - dumpedNodes.insert(noret); - dumpNode(out, *noret, nullptr, "noret"); - } - if (auto *ret = params.getReturn()) { - dumpedNodes.insert(ret); - dumpNode(out, *ret, nullptr, "ret"); - } - out << " }\n"; - } - - void bindParamsToCall(std::ostream& out, - sdg::DGParameters& params, - sdg::DGNode *call) const { - /// input parameters - for (auto& param : params) { - auto& nd = param.getOutputArgument(); - out << " " << *call << " -> " << nd << "[style=dashed]\n"; - } - - if (auto *noret = params.getNoReturn()) { - out << " " << *call << " -> " << *noret << "[style=dashed]\n"; - } - if (auto *ret = params.getReturn()) { - out << " " << *call << " -> " << *ret << "[style=dashed]\n"; - } - } - -public: - SDG2Dot(SystemDependenceGraph *sdg) : _llvmsdg(sdg) {} - - void dump(const std::string& file) const { - std::ofstream out(file.c_str(),std::ofstream::out); - std::set calls; - - out << "digraph SDG {\n"; - out << " compound=\"true\"\n"; - - for (auto *dg : _llvmsdg->getSDG()) { - /// - // Dependence graphs (functions) - out << " subgraph cluster_dg_" << dg->getID() << " {\n"; - out << " color=black;\n"; - out << " style=filled;\n"; - out << " fillcolor=grey95;\n"; - out << " label=\"" << dg->getName() << " (id " << dg->getID() << ")\";\n"; - out << "\n"; - - /// - // Parameters of the DG - // - /// Formal input parameters - dumpParams(out, dg->getParameters(), "formal parameters"); - - /// - // Basic blocks - for (auto *blk : dg->getBBlocks()) { - out << " subgraph cluster_dg_" << dg->getID() << "_bb_" - << blk->getID() << " {\n"; - out << " label=\"bblock #" << blk->getID() << "\"\n"; - for (auto *nd : blk->getNodes()) { - dumpedNodes.insert(nd); - dumpNode(out, *nd); - - if (auto *C = sdg::DGNodeCall::get(nd)) { - // store the node for later use (dumping of call edges etc.) - calls.insert(C); - // dump actual parameters - dumpParams(out, C->getParameters(), "actual parameters"); - } - } - out << " }\n"; - } - - /// - // -- edges -- - out << " /* edges */\n"; - for (auto *nd : dg->getNodes()) { - for (auto *use : nd->uses()) { - out << " " << *nd << " -> " << *use - << "[style=\"dashed\"]\n"; - } - for (auto *def : nd->memdep()) { - out << " " << *def << " -> " << *nd << "[color=red]\n"; - } - for (auto *ctrl : nd->controls()) { - out << " " << *nd << " -> " << *ctrl << "[color=blue]\n"; - } - } - out << " /* block edges */\n"; - for (auto *blk : dg->getBBlocks()) { - for (auto *ctrl : blk->controls()) { - out << " " << *blk->back() << " -> "; - if (auto *ctrlB = sdg::DGBBlock::get(ctrl)) { - out << *ctrlB->front(); - } else { - out << *ctrl; - } - - out << "[color=blue penwidth=2 " - << " ltail=cluster_dg_" << dg->getID() << "_bb_" << blk->getID(); - - if (ctrl->getType() == sdg::DGElementType::BBLOCK) { - out << " lhead=cluster_dg_" << dg->getID() << "_bb_" << ctrl->getID(); - } - out << "]\n"; - } - } - - out << " }\n"; - - dumpedNodes.clear(); - } - - //// - // -- Interprocedural edges -- - - if (!calls.empty()) { - out << " /* call and param edges */\n"; - } - for (auto *C : calls) { - bindParamsToCall(out, C->getParameters(), C); - for (auto *dg : C->getCallees()) { - out << " " << *C - << " -> " << *dg->getFirstNode() - << "[lhead=cluster_dg_" << dg->getID() - << " label=\"call '" << dg->getName()<< "'\"" - << " style=dashed penwidth=3]\n"; - } - } - - out << "}\n"; - } -}; - -} // namespace llvmdg -} // namespace dg - -#endif // DG_LLVM_SDG2DOT_H_ diff --git a/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraph.h b/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraph.h deleted file mode 100644 index f050a4cdf..000000000 --- a/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraph.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef DG_LLVM_SYSTEM_DEPENDNECE_GRAPH_H_ -#define DG_LLVM_SYSTEM_DEPENDNECE_GRAPH_H_ - -#include - -#include "dg/SystemDependenceGraph/SystemDependenceGraph.h" -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#include "dg/llvm/DataDependence/DataDependence.h" -#include "dg/llvm/ControlDependence/ControlDependence.h" -#include "dg/llvm/LLVMAnalysisOptions.h" - -namespace llvm { - class Module; - class Value; -} - -namespace dg { -namespace llvmdg { - -class SystemDependenceGraphOptions : public LLVMAnalysisOptions { -}; - -/* FIXME: hide this from the world */ -struct SDGBuilder; - -class SystemDependenceGraph { - const SystemDependenceGraphOptions _options; - llvm::Module *_module; - sdg::SystemDependenceGraph _sdg; - LLVMPointerAnalysis *_pta{nullptr}; - dda::LLVMDataDependenceAnalysis *_dda{nullptr}; - LLVMControlDependenceAnalysis *_cda{nullptr}; - - //SystemDependenceGraphBuilder _builder; - // FIXME: do this unordered maps - std::map _mapping; - std::map _rev_mapping; - // built functions - std::map _fun_mapping; - std::map _blk_mapping; - - void buildNodes(); - void buildEdges(); - void buildSDG(); - - void addMapping(llvm::Value *v, sdg::DGElement *n) { - assert(_mapping.find(v) == _mapping.end() && - "Already have this value"); - _mapping[v] = n; - _rev_mapping[n] = v; - } - - void addFunMapping(llvm::Function *F, sdg::DependenceGraph *g) { - assert(_mapping.find(F) == _mapping.end() && - "Already have this function"); - _fun_mapping[F] = g; - } - - void addBlkMapping(llvm::BasicBlock *b, sdg::DGBBlock *dgb) { - assert(_blk_mapping.find(b) == _blk_mapping.end() && - "Already have this block"); - _blk_mapping[b] = dgb; - } - - friend struct SDGBuilder; - -public: - SystemDependenceGraph(llvm::Module *M, - LLVMPointerAnalysis *PTA, - dda::LLVMDataDependenceAnalysis *DDA, - LLVMControlDependenceAnalysis *CDA, - const SystemDependenceGraphOptions& opts = {}) - : _options(opts), _module(M), _sdg(), _pta(PTA), _dda(DDA), _cda(CDA) { - buildSDG(); - } - - llvm::Module *getModule() { return _module; } - const llvm::Module *getModule() const { return _module; } - - sdg::DGElement* getNode(const llvm::Value *v) const { - auto it = _mapping.find(v); - return it == _mapping.end() ? nullptr : it->second; - } - - sdg::DGBBlock* getBBlock(const llvm::BasicBlock *b) const { - auto it = _blk_mapping.find(b); - return it == _blk_mapping.end() ? nullptr : it->second; - } - - llvm::Value* getValue(const sdg::DGElement *n) const { - auto it = _rev_mapping.find(n); - return it == _rev_mapping.end() ? nullptr : it->second; - } - - sdg::DependenceGraph* getDG(const llvm::Function *F) const { - auto it = _fun_mapping.find(F); - return it == _fun_mapping.end() ? nullptr : it->second; - } - - sdg::SystemDependenceGraph& getSDG() { return _sdg; } - const sdg::SystemDependenceGraph& getSDG() const { return _sdg; } -}; - -} // namespace llvmdg -} // namespace dg - -#endif // DG_LLVM_SYSTEM_DEPENDNECE_GRAPH_H_ diff --git a/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraphBuilder.h b/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraphBuilder.h deleted file mode 100644 index 8dcf062c8..000000000 --- a/include/dg/llvm/SystemDependenceGraph/SystemDependenceGraphBuilder.h +++ /dev/null @@ -1,226 +0,0 @@ -#ifndef DG_LLVM_SYSTEM_DEPENDENCE_GRAPH_BUILDER_H_ -#define DG_LLVM_SYSTEM_DEPENDENCE_GRAPH_BUILDER_H_ - -#include -#include // std::clock - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -#include "dg/llvm/LLVMDependenceGraph.h" -#include "dg/llvm/PointerAnalysis/LLVMPointerAnalysisOptions.h" -#include "dg/llvm/DataDependence/DataDependence.h" -#include "dg/llvm/DataDependence/LLVMDataDependenceAnalysisOptions.h" - -#include "dg/llvm/PointerAnalysis/PointerAnalysis.h" -#ifdef HAVE_SVF -#include "dg/llvm/PointerAnalysis/SVFPointerAnalysis.h" -#endif -#include "dg/PointerAnalysis/PointerAnalysisFI.h" -#include "dg/PointerAnalysis/PointerAnalysisFS.h" -#include "dg/PointerAnalysis/PointerAnalysisFSInv.h" -#include "dg/PointerAnalysis/Pointer.h" -#include "dg/Offset.h" - -namespace llvm { - class Module; - class Function; -} - -namespace dg { -namespace llvmdg { - -class LLVMPointerAnalysisOptions; -class LLVMDataDependenceAnalysisOptions; - -class SystemDependenceGraphBuilder { - llvm::Module *_M; - const SystemDependenceGraphOptions _options; - std::unique_ptr _PTA{}; - std::unique_ptr _DDA{nullptr}; - std::unique_ptr _sdg{}; - std::unique_ptr _controlFlowGraph{}; - llvm::Function *_entryFunction{nullptr}; - - struct Statistics { - uint64_t cdTime{0}; - uint64_t ptaTime{0}; - uint64_t rdaTime{0}; - uint64_t inferaTime{0}; - uint64_t joinsTime{0}; - uint64_t critsecTime{0}; - } _statistics; - - std::clock_t _time_start; - void _timerStart() { _time_start = std::clock(); } - uint64_t _timerEnd() { return (std::clock() - _time_start); } - - void _runPointerAnalysis() { - assert(_PTA && "BUG: No PTA"); - - _timerStart(); - _PTA->run(); - _statistics.ptaTime = _timerEnd(); - } - - void _runDataDependenceAnalysis() { - assert(_DDA && "BUG: No RD"); - - _timerStart(); - _DDA->run(); - _statistics.rdaTime = _timerEnd(); - } - - void _runControlDependenceAnalysis() { - _timerStart(); - _sdg->computeControlDependencies(_options.cdAlgorithm, - _options.interprocCd); - _statistics.cdTime = _timerEnd(); - } - - void _runInterferenceDependenceAnalysis() { - _timerStart(); - _sdg->computeInterferenceDependentEdges(_controlFlowGraph.get()); - _statistics.inferaTime = _timerEnd(); - } - - void _runForkJoinAnalysis() { - _timerStart(); - _sdg->computeForkJoinDependencies(_controlFlowGraph.get()); - _statistics.joinsTime = _timerEnd(); - } - - void _runCriticalSectionAnalysis() { - _timerStart(); - _sdg->computeCriticalSections(_controlFlowGraph.get()); - _statistics.critsecTime = _timerEnd(); - } - - bool verify() const { - return _sdg->verify(); - } - -public: - SystemDependenceGraphBuilder(llvm::Module *M) - : SystemDependenceGraphBuilder(M, {}) {} - - SystemDependenceGraphBuilder(llvm::Module *M, - const SystemDependenceGraphOptions& opts) - : _M(M), _options(opts), - _PTA(createPTA()), - _DDA(new LLVMDataDependenceAnalysis(M, _PTA.get(), - _options.DDAOptions)), - _sdg(new LLVMDependenceGraph(opts.threads)), - _controlFlowGraph(_options.threads && !_options.PTAOptions.isSVF() ? // check SVF due to the static cast... - new ControlFlowGraph(static_cast(_PTA.get())) : nullptr), - _entryFunction(M->getFunction(_options.entryFunction)) { - assert(_entryFunction && "The entry function not found"); - } - - LLVMPointerAnalysis *createPTA() { - if (_options.PTAOptions.isSVF()) - return new SVFPointerAnalysis(_M, _options.PTAOptions); - - return new DGLLVMPointerAnalysis(_M, _options.PTAOptions); - } - - LLVMPointerAnalysis *getPTA() { return _PTA.get(); } - LLVMDataDependenceAnalysis *getRDA() { return _DDA.get(); } - - const Statistics& getStatistics() const { return _statistics; } - - // construct the whole graph with all edges - std::unique_ptr&& build() { - // compute data dependencies - _runPointerAnalysis(); - _runDataDependenceAnalysis(); - - // build the graph itself (the nodes, but without edges) - _sdg->build(_M, _PTA.get(), _DDA.get(), _entryFunction); - - // insert the data dependencies edges - _sdg->addDefUseEdges(); - - // compute and fill-in control dependencies - _runControlDependenceAnalysis(); - - if (_options.threads) { - if (_options.PTAOptions.isSVF()) { - assert(0 && "Threading needs the DG pointer analysis, SVF is not supported yet"); - abort(); - } - _controlFlowGraph->buildFunction(_entryFunction); - _runInterferenceDependenceAnalysis(); - _runForkJoinAnalysis(); - _runCriticalSectionAnalysis(); - } - - // verify if the graph is built correctly - if (_options.verifyGraph && !_sdg->verify()) { - _sdg.reset(); - return std::move(_sdg); - } - - return std::move(_sdg); - } - - // Build only the graph with CFG edges. - // No dependencies between instructions are added. - // The dependencies must be filled in by calling computeDependencies() - // later. - // NOTE: this function still runs pointer analysis as it is needed - // for sound construction of CFG in the presence of function pointer calls. - std::unique_ptr&& constructCFGOnly() { - // data dependencies - _runPointerAnalysis(); - - // build the graph itself - _sdg->build(_M, _PTA.get(), _DDA.get(), _entryFunction); - - if (_options.threads) { - _controlFlowGraph->buildFunction(_entryFunction); - } - - // verify if the graph is built correctly - if (_options.verifyGraph && !_sdg->verify()) { - _sdg.reset(); - return std::move(_sdg); - } - - return std::move(_sdg); - } - - // This method serves to finish the graph construction - // after constructCFGOnly was used to build the graph. - // This function takes the dg (returned from the constructCFGOnly) - // and retains its ownership until it computes the edges. - // Then it returns the ownership back to the caller. - std::unique_ptr&& - computeDependencies(std::unique_ptr&& dg) { - // get the ownership - _sdg = std::move(dg); - - // data-dependence edges - _runDataDependenceAnalysis(); - _sdg->addDefUseEdges(); - - // fill-in control dependencies - _runControlDependenceAnalysis(); - - if (_options.threads) { - _runInterferenceDependenceAnalysis(); - _runForkJoinAnalysis(); - _runCriticalSectionAnalysis(); - } - - return std::move(_sdg); - } - -}; - -} // namespace llvmdg -} // namespace dg - -#endif // DG_LLVM_SYSTEM_DEPENDENCE_GRAPH_BUILDER_H_ diff --git a/include/dg/llvm/ThreadRegions/ControlFlowGraph.h b/include/dg/llvm/ThreadRegions/ControlFlowGraph.h deleted file mode 100644 index cbef392f3..000000000 --- a/include/dg/llvm/ThreadRegions/ControlFlowGraph.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef CONTROLFLOWGRAPH_H -#define CONTROLFLOWGRAPH_H - -#include -#include - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -SILENCE_LLVM_WARNINGS_POP - -namespace dg { - class DGLLVMPointerAnalysis; -} - -namespace llvm { - class Function; -} - -class ThreadRegion; -class GraphBuilder; -class ThreadRegionsBuilder; -class CriticalSectionsBuilder; - -class ControlFlowGraph -{ -private: - std::unique_ptr graphBuilder; - std::unique_ptr threadRegionsBuilder; - std::unique_ptr criticalSectionsBuilder; - -public: - ControlFlowGraph(dg::DGLLVMPointerAnalysis * pointsToAnalysis); - - ~ControlFlowGraph(); - - std::set getJoins() const; - - std::set getCorrespondingForks(const llvm::CallInst * callInst) const; - - std::set getLocks() const; - - std::set getCorrespongingUnlocks(const llvm::CallInst * callInst) const; - - std::set getCorrespondingCriticalSection(const llvm::CallInst * callInst) const; - - void buildFunction(const llvm::Function *function); - - void printWithRegions(std::ostream & ostream) const; - - void printWithoutRegions(std::ostream & ostream) const; - - std::set threadRegions(); -}; - -#endif // CONTROLFLOWGRAPH_H diff --git a/include/dg/llvm/ThreadRegions/Graphs/BlockGraph.h b/include/dg/llvm/ThreadRegions/Graphs/BlockGraph.h deleted file mode 100644 index b052c8f1a..000000000 --- a/include/dg/llvm/ThreadRegions/Graphs/BlockGraph.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef BLOCKGRAPH_H -#define BLOCKGRAPH_H - -#include -#include -#include - -namespace llvm { - class BasicBlock; -} - -class Node; - -class BlockGraph -{ -private: - const llvm::BasicBlock * llvmBlock_ = nullptr; - - Node * firstNode_ = nullptr; - Node * lastNode_ = nullptr; - -public: - BlockGraph(const llvm::BasicBlock *llvmBlock, Node * firstNode, Node * lastNode); - - BlockGraph(const BlockGraph &) = delete; - - BlockGraph & operator==(const BlockGraph &) = delete; - - const llvm::BasicBlock * llvmBlock() const; - - Node * firstNode() const; - Node * lastNode() const; -}; - -#endif // BLOCKGRAPH_H diff --git a/include/dg/llvm/ThreadRegions/Graphs/CriticalSectionsBuilder.h b/include/dg/llvm/ThreadRegions/Graphs/CriticalSectionsBuilder.h deleted file mode 100644 index 124023433..000000000 --- a/include/dg/llvm/ThreadRegions/Graphs/CriticalSectionsBuilder.h +++ /dev/null @@ -1,76 +0,0 @@ -#ifndef CRITICALSECTIONSBUILDER_H -#define CRITICALSECTIONSBUILDER_H - -#include -#include -#include - -class Node; -class LockNode; -class UnlockNode; -class ExitNode; -class ForkNode; - -namespace llvm { - class Instruction; - class CallInst; -} - -class CriticalSection { -private: - LockNode * lock_; - std::set nodes_; - -public: - CriticalSection(LockNode * lock); - - const llvm::CallInst * lock() const; - - std::set nodes() const; - - std::set unlocks() const; - - bool insertNodes(const std::set & nodes_); -}; - -class CriticalSectionsBuilder -{ - std::set locks_; - - LockNode * currentLock; - std::set currentUnlocks; - - std::set visited_; - std::set examined_; - - std::map criticalSections_; -public: - CriticalSectionsBuilder(); - - ~CriticalSectionsBuilder(); - - bool buildCriticalSection(LockNode * lock); - - std::set locks() const; - - std::set correspondingNodes(const llvm::CallInst * lock) const; - - std::set correspondingUnlocks(const llvm::CallInst * lock) const; - -private: - bool populateCriticalSection(); - - void visitNode(Node * node); - - void preVisit(Node * node); - - void visit(Node * node); - - void postVisit(Node * node); - - bool visited(Node * node) const; - - bool examined(Node * node) const; -}; - -#endif // CRITICALSECTIONSBUILDER_H diff --git a/include/dg/llvm/ThreadRegions/Graphs/FunctionGraph.h b/include/dg/llvm/ThreadRegions/Graphs/FunctionGraph.h deleted file mode 100644 index 1433b0610..000000000 --- a/include/dg/llvm/ThreadRegions/Graphs/FunctionGraph.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef FUNCTIONGRAPH_H -#define FUNCTIONGRAPH_H - -namespace llvm { - class Function; -} - -class EntryNode; -class ExitNode; - -class FunctionGraph -{ -private: - const llvm::Function * llvmFunction_ = nullptr; - - EntryNode * entryNode_ = nullptr; - ExitNode * exitNode_ = nullptr; - -public: - FunctionGraph(const llvm::Function *llvmFunction, EntryNode *entryNode, ExitNode *exitNode); - - FunctionGraph(const FunctionGraph &) = delete; - - FunctionGraph & operator==(const FunctionGraph &) = delete; - - const llvm::Function *llvmFunction() const; - - EntryNode * entryNode() const; - ExitNode * exitNode() const; -}; - -#endif // FUNCTIONGRAPH_H diff --git a/include/dg/llvm/ThreadRegions/Graphs/GraphBuilder.h b/include/dg/llvm/ThreadRegions/Graphs/GraphBuilder.h deleted file mode 100644 index f22422837..000000000 --- a/include/dg/llvm/ThreadRegions/Graphs/GraphBuilder.h +++ /dev/null @@ -1,145 +0,0 @@ -#ifndef GRAPHBUILDER_H -#define GRAPHBUILDER_H - -#include -#include -#include -#include -#include - -namespace dg { - class DGLLVMPointerAnalysis; - namespace analysis { - namespace pta { - class PSNodeJoin; - } - } -} - -namespace llvm { - class Instruction; - class BasicBlock; - class Function; - class CallInst; -} - -class Node; -class ForkNode; -class JoinNode; -class LockNode; -class UnlockNode; -class BlockGraph; -class FunctionGraph; - -class GraphBuilder -{ -private: - dg::DGLLVMPointerAnalysis * pointsToAnalysis_ = nullptr; - - std::unordered_set artificialNodes_; - std::unordered_map llvmToNodeMap_; - - std::unordered_map llvmToBlockMap_; - std::unordered_map llvmToFunctionMap_; - - std::unordered_map llvmToJoins_; - std::unordered_map llvmToForks_; - - std::unordered_map llvmToLocks_; - std::unordered_map llvmToUnlocks_; - -public: - using NodeSequence = std::pair; - - GraphBuilder(dg::DGLLVMPointerAnalysis * pointsToAnalysis); - - ~GraphBuilder(); - - auto size() const -> decltype (llvmToNodeMap_.size() + artificialNodes_.size()) { - return llvmToNodeMap_.size() + artificialNodes_.size(); - } - - NodeSequence buildInstruction(const llvm::Instruction * instruction); - - NodeSequence buildBlock(const llvm::BasicBlock * basicBlock); - - NodeSequence buildFunction(const llvm::Function * function); - - Node * findInstruction(const llvm::Instruction * instruction); - - BlockGraph * findBlock(const llvm::BasicBlock * basicBlock); - - FunctionGraph * findFunction(const llvm::Function * function); - - std::set getJoins() const; - std::set getCorrespondingForks(const llvm::CallInst *callInst) const; - - std::set getLocks() const; - - bool matchForksAndJoins(); - - bool matchLocksAndUnlocks(); - - void print(std::ostream & ostream) const; - - void printNodes(std::ostream & ostream) const; - - void printEdges(std::ostream & ostream) const; - - void clear(); - -private: - NodeSequence buildCallInstruction(const llvm::Instruction * instruction); - - NodeSequence buildReturnInstruction(const llvm::Instruction * instruction); - - NodeSequence buildGeneralInstruction(const llvm::Instruction * instruction); - - NodeSequence buildGeneralCallInstruction(const llvm::CallInst * callInstruction); - - NodeSequence insertFunction(const llvm::Function * function, const llvm::CallInst *callInstruction); - - NodeSequence insertFunctionPointerCall(const llvm::CallInst *callInstruction); - - NodeSequence insertUndefinedFunction(const llvm::Function * function, const llvm::CallInst *callInstruction); - - NodeSequence insertPthreadCreate(const llvm::CallInst * callInstruction); - - NodeSequence insertPthreadMutexLock(const llvm::CallInst * callInstruction); - - NodeSequence insertPthreadMutexUnlock(const llvm::CallInst * callInstruction); - - NodeSequence insertPthreadJoin(const llvm::CallInst * callInstruction); - - NodeSequence insertPthreadExit(const llvm::CallInst * callInstruction); - - NodeSequence createOrGetFunction(const llvm::Function * function); - - - template - T * addNode(T * node) { - if (node->isArtificial()) { - if (this->artificialNodes_.insert(node).second) { - return node; - } - } else { - if (this->llvmToNodeMap_.emplace(node->llvmInstruction(), node).second) { - return node; - } - } - return nullptr; - } -}; - -int predecessorsNumber(const llvm::BasicBlock * basicBlock); - -int successorsNumber(const llvm::BasicBlock * basicBlock); - -bool isReachable(const llvm::BasicBlock * basicBlock); - -template -const llvm::CallInst *getCallInst(T *PSNode) { - return PSNode->callInst()->template getUserData(); -} - -#endif // GRAPHBUILDER_H diff --git a/include/dg/llvm/ThreadRegions/Graphs/ThreadRegionsBuilder.h b/include/dg/llvm/ThreadRegions/Graphs/ThreadRegionsBuilder.h deleted file mode 100644 index 34effc5c9..000000000 --- a/include/dg/llvm/ThreadRegions/Graphs/ThreadRegionsBuilder.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef THREADREGIONBUILDER_H -#define THREADREGIONBUILDER_H - -#include -#include -#include - -#include "dg/llvm/ThreadRegions/ThreadRegion.h" - -class Node; -class ForkNode; -class ExitNode; - -class ThreadRegionsBuilder -{ -private: - std::unordered_map visitedNodeToRegionMap; - std::unordered_map examinedNodeToRegionMap; - - std::set threadRegions_; -public: - ThreadRegionsBuilder(std::size_t size = 0); - - ~ThreadRegionsBuilder(); - - void build(Node * node); - - ThreadRegion * region(Node * node) const; - - void printNodes(std::ostream & ostream) const; - - void printEdges(std::ostream & ostream) const; - - void reserve(std::size_t size); - - void clear(); - - std::set threadRegions(); - -private: - void visit(Node * node); - - bool unvisited(Node * node) const; - - bool visited(Node * node) const; - - bool examined(Node * node) const; - - bool examined(ThreadRegion * region) const; - - void populateThreadRegions(); - - void clearComputingData(); - - ThreadRegion * regionOfVisitedNode(Node * node) const; - - ThreadRegion * regionOfExaminedNode(Node * node) const; - - bool shouldCreateNewRegion(Node * caller, Node * successor) const; -}; - -#endif // THREADREGIONBUILDER_H diff --git a/include/dg/llvm/ThreadRegions/MayHappenInParallel.h b/include/dg/llvm/ThreadRegions/MayHappenInParallel.h deleted file mode 100644 index e2c1d18bf..000000000 --- a/include/dg/llvm/ThreadRegions/MayHappenInParallel.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MAYHAPPENINPARALLEL_H -#define MAYHAPPENINPARALLEL_H - -#include - -#include "ThreadRegion.h" - -class MayHappenInParallel -{ -private: - std::set threadRegions_; -public: - MayHappenInParallel(std::set threadRegions); - - std::set parallelRegions(ThreadRegion * threadRegion); -}; - -#endif // MAYHAPPENINPARALLEL_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/CallFuncPtrNode.h b/include/dg/llvm/ThreadRegions/Nodes/CallFuncPtrNode.h deleted file mode 100644 index 3c59b2995..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/CallFuncPtrNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CALLFUNCPTRNODE_H -#define CALLFUNCPTRNODE_H - -#include "Node.h" - -class CallFuncPtrNode : public Node -{ -public: - CallFuncPtrNode(const llvm::Instruction * instruction); -}; - -#endif // CALLFUNCPTRNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/CallNode.h b/include/dg/llvm/ThreadRegions/Nodes/CallNode.h deleted file mode 100644 index 6f9ae78e1..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/CallNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CALLNODE_H -#define CALLNODE_H - -#include "Node.h" - -class CallNode : public Node -{ -public: - CallNode(const llvm::Instruction * instruction = nullptr, const llvm::CallInst * callInst = nullptr); -}; - -#endif // CALLNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/CallReturnNode.h b/include/dg/llvm/ThreadRegions/Nodes/CallReturnNode.h deleted file mode 100644 index 19b06ac71..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/CallReturnNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef CALLRETURNNODE_H -#define CALLRETURNNODE_H - -#include "Node.h" - -class CallReturnNode : public Node -{ -public: - CallReturnNode(); -}; - -#endif // CALLRETURNNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/EntryNode.h b/include/dg/llvm/ThreadRegions/Nodes/EntryNode.h deleted file mode 100644 index 3495a5dd1..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/EntryNode.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef ENTRYNODE_H -#define ENTRYNODE_H - -#include "Node.h" - -#include - -class ForkNode; - -class EntryNode : public Node -{ -private: - std::set forkPredecessors_; -public: - EntryNode(); - - bool addForkPredecessor(ForkNode *forkNode); - - bool removeForkPredecessor(ForkNode *forkNode); - - const std::set & forkPredecessors() const; - std::set forkPredecessors(); - - std::size_t predecessorsNumber() const override; - - friend class ForkNode; -}; - -#endif // ENTRYNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/ExitNode.h b/include/dg/llvm/ThreadRegions/Nodes/ExitNode.h deleted file mode 100644 index 2c21131fa..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/ExitNode.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef EXITNODE_H -#define EXITNODE_H - -#include "Node.h" - -#include - -class JoinNode; - -class ExitNode : public Node -{ -private: - std::set joinSuccessors_; -public: - ExitNode(); - - bool addJoinSuccessor(JoinNode *joinNode); - - bool removeJoinSuccessor(JoinNode *joinNode); - - const std::set & joinSuccessors() const; - std::set joinSuccessors(); - - std::size_t successorsNumber() const override; - - void printOutcomingEdges(std::ostream &ostream) const override; - - friend class JoinNode; -}; - -#endif // EXITNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/ForkNode.h b/include/dg/llvm/ThreadRegions/Nodes/ForkNode.h deleted file mode 100644 index 05a6dddd9..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/ForkNode.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef FORKNODE_H -#define FORKNODE_H - -#include "Node.h" - -class EntryNode; -class JoinNode; - -class ForkNode : public Node -{ - std::set forkSuccessors_; - std::set correspondingJoins_; -public: - ForkNode(const llvm::Instruction * instruction = nullptr, const llvm::CallInst * callInst = nullptr); - - bool addCorrespondingJoin(JoinNode * joinNode); - - bool addForkSuccessor(EntryNode * entryNode); - - bool removeForkSuccessor(EntryNode * entryNode); - - const std::set & forkSuccessors() const; - std::set forkSuccessors(); - - std::size_t successorsNumber() const override; - - const std::set & correspondingJoins() const; - std::set correspondingJoins(); - - void printOutcomingEdges(std::ostream &ostream) const override; - - friend class EntryNode; - friend class JoinNode; -}; - -#endif // FORKNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/GeneralNode.h b/include/dg/llvm/ThreadRegions/Nodes/GeneralNode.h deleted file mode 100644 index 4809ca457..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/GeneralNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef GENERALNODE_H -#define GENERALNODE_H - -#include "Node.h" - -class GeneralNode : public Node -{ -public: - GeneralNode(const llvm::Instruction * instruction = nullptr); -}; - -#endif // GENERALNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/JoinNode.h b/include/dg/llvm/ThreadRegions/Nodes/JoinNode.h deleted file mode 100644 index c9c790cf8..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/JoinNode.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef JOINNODE_H -#define JOINNODE_H - -#include "Node.h" - -#include - -class ExitNode; -class ForkNode; - -class JoinNode : public Node -{ -private: - std::set joinPredecessors_; - std::set correspondingForks_; -public: - JoinNode(const llvm::Instruction *value = nullptr, const llvm::CallInst * callInst = nullptr); - - bool addCorrespondingFork(ForkNode * forkNode); - - bool addJoinPredecessor(ExitNode *exitNode); - - bool removeJoinPredecessor(ExitNode *exitNode); - - const std::set & joinPredecessors() const; - std::set joinPredecessors(); - - std::size_t predecessorsNumber() const override; - - const std::set & correspondingForks() const; - std::set correspondingForks(); - - friend class ExitNode; - friend class ForkNode; -}; - -#endif // JOINNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/LockNode.h b/include/dg/llvm/ThreadRegions/Nodes/LockNode.h deleted file mode 100644 index 549b4d6fa..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/LockNode.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef LOCKNODE_H -#define LOCKNODE_H - -#include "Node.h" - -class UnlockNode; - -class LockNode : public Node -{ - std::set correspondingUnlocks_; -public: - LockNode(const llvm::Instruction * instruction = nullptr, const llvm::CallInst * callInst = nullptr); - - bool addCorrespondingUnlock(UnlockNode * unlockNode); - - const std::set & correspondingUnlocks() const; - std::set correspondingUnlocks(); -}; - -#endif // LOCKNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/Node.h b/include/dg/llvm/ThreadRegions/Nodes/Node.h deleted file mode 100644 index d1991ba24..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/Node.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef NODE_H -#define NODE_H - -#include -#include -#include - -#include "NodeIterator.h" - -namespace llvm { - class CallInst; - class Instruction; -} - -enum class NodeType { GENERAL, FORK, JOIN, LOCK, UNLOCK, ENTRY, EXIT, CALL, CALL_FUNCPTR, CALL_RETURN, RETURN }; - -inline std::string nodeTypeToString(enum NodeType type) -{ -#define ELEM(t) case t: do {return (#t); }while(0); break; - switch(type) { - ELEM(NodeType::GENERAL) - ELEM(NodeType::FORK) - ELEM(NodeType::JOIN) - ELEM(NodeType::LOCK) - ELEM(NodeType::UNLOCK) - ELEM(NodeType::ENTRY) - ELEM(NodeType::EXIT) - ELEM(NodeType::CALL) - ELEM(NodeType::CALL_RETURN) - ELEM(NodeType::CALL_FUNCPTR) - ELEM(NodeType::RETURN) - }; -#undef ELEM - return "undefined"; -} - -class Node -{ -private: - const int id_; - const NodeType nodeType_; - const llvm::Instruction * llvmInstruction_; - const llvm::CallInst * callInstruction_; - std::set predecessors_; - std::set successors_; - - static int lastId; - -public: - Node(NodeType type, const llvm::Instruction * instruction = nullptr, const llvm::CallInst * callInst = nullptr); - - virtual ~Node() = default; - - NodeIterator begin() const; - - NodeIterator end() const; - - int id() const; - - NodeType getType() const; - - std::string dotName() const; - - bool addPredecessor(Node *); - bool addSuccessor(Node *); - - bool removePredecessor(Node *); - bool removeSuccessor(Node *); - - const std::set & predecessors() const; - const std::set & successors() const; - - virtual std::size_t predecessorsNumber() const; - virtual std::size_t successorsNumber() const; - - bool isArtificial() const; - - const llvm::Instruction *llvmInstruction() const; - - const llvm::CallInst * callInstruction() const; - - std::string dump() const; - - std::string label() const; - - virtual void printOutcomingEdges(std::ostream & ostream) const; -}; - - - - -#endif // NODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/NodeIterator.h b/include/dg/llvm/ThreadRegions/Nodes/NodeIterator.h deleted file mode 100644 index b5a6dfc90..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/NodeIterator.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NODEITERATOR_H -#define NODEITERATOR_H - -#include - -class Node; -class ForkNode; -class JoinNode; -class EntryNode; -class ExitNode; - -class NodeIterator { - -private: - const ForkNode * forkNode = nullptr; - const ExitNode * exitNode = nullptr; - std::set::iterator successorsIterator; - std::set::iterator forkSuccessorsIterator; - std::set::iterator joinSuccessorsIterator; - -public: - explicit NodeIterator(const Node * node = nullptr, bool begin = true); - - NodeIterator & operator++(); - NodeIterator operator++(int); - bool operator==(const NodeIterator & other) const; - bool operator!=(const NodeIterator & other) const; - Node * operator*() const; -}; - -#endif // NODEITERATOR_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/Nodes.h b/include/dg/llvm/ThreadRegions/Nodes/Nodes.h deleted file mode 100644 index ce4e6e577..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/Nodes.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef NODES_H -#define NODES_H - -#include "GeneralNode.h" -#include "ForkNode.h" -#include "JoinNode.h" -#include "LockNode.h" -#include "UnlockNode.h" -#include "EntryNode.h" -#include "ExitNode.h" -#include "CallNode.h" -#include "CallFuncPtrNode.h" -#include "CallReturnNode.h" -#include "ReturnNode.h" - -template struct Map; - -template <> struct Map { - using type = GeneralNode; - using const_type = const GeneralNode; -}; - -template <> struct Map { - using type = ForkNode; - using const_type = const ForkNode; -}; - -template <> struct Map { - using type = JoinNode; - using const_type = const JoinNode; -}; - -template <> struct Map { - using type = LockNode; - using const_type = const LockNode; -}; - -template <> struct Map { - using type = UnlockNode; - using const_type = const UnlockNode; -}; - -template <> struct Map { - using type = EntryNode; - using const_type = const EntryNode; -}; - -template <> struct Map { - using type = ExitNode; - using const_type = const ExitNode; -}; - -template <> struct Map { - using type = CallNode; - using const_type = const CallNode; -}; - -template <> struct Map { - using type = CallFuncPtrNode; - using const_type = const CallFuncPtrNode; -}; - -template <> struct Map { - using type = CallReturnNode; - using const_type = const CallReturnNode; -}; - -template <> struct Map { - using type = ReturnNode; - using const_type = const ReturnNode; -}; - -template -typename Map::type * createNode(Args... args) { - return new typename Map::type(args...); -} - -template -typename Map::type * castNode(T * node) { - if (!node) { - return nullptr; - } - if (node->getType() == nodeType) { - return static_cast::type *>(node); - } else { - return nullptr; - } -} - -template -typename Map::const_type * castNode(const T * node) { - if (!node) { - return nullptr; - } - if (node->getType() == nodeType) { - return static_cast::const_type *>(node); - } else { - return nullptr; - } -} - -#endif // NODES_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/ReturnNode.h b/include/dg/llvm/ThreadRegions/Nodes/ReturnNode.h deleted file mode 100644 index dd00feb89..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/ReturnNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef RETURNNODE_H -#define RETURNNODE_H - -#include "Node.h" - -class ReturnNode : public Node -{ -public: - ReturnNode(const llvm::Instruction * instruction = nullptr); -}; - -#endif // RETURNNODE_H diff --git a/include/dg/llvm/ThreadRegions/Nodes/UnlockNode.h b/include/dg/llvm/ThreadRegions/Nodes/UnlockNode.h deleted file mode 100644 index 31043774b..000000000 --- a/include/dg/llvm/ThreadRegions/Nodes/UnlockNode.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef UNLOCKNODE_H -#define UNLOCKNODE_H - -#include "Node.h" - -class UnlockNode : public Node -{ -public: - UnlockNode(const llvm::Instruction * instruction = nullptr, const llvm::CallInst * callInst = nullptr); -}; - -#endif // UNLOCKNODE_H diff --git a/include/dg/llvm/ThreadRegions/ThreadRegion.h b/include/dg/llvm/ThreadRegions/ThreadRegion.h deleted file mode 100644 index ac7b07a68..000000000 --- a/include/dg/llvm/ThreadRegions/ThreadRegion.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef THREADREGION_H -#define THREADREGION_H - -#include -#include - -class Node; -class ControlFlowGraph; - -namespace llvm { - class Instruction; -} - -class ThreadRegion -{ - int id_; - Node * foundingNode_; - std::set nodes_; - std::set predecessors_; - std::set successors_; - - static int lastId; - -public: - ThreadRegion(Node * node); - - int id() const; - - bool addPredecessor(ThreadRegion * predecessor); - bool addSuccessor(ThreadRegion * successor); - - bool removePredecessor(ThreadRegion * predecessor); - bool removeSuccessor(ThreadRegion * successor); - - const std::set & predecessors() const; - std::set predecessors(); - - const std::set & successors() const; - std::set successors(); - - bool insertNode(Node * node); - bool removeNode(Node * node); - - Node * foundingNode() const; - - const std::set &nodes() const; - std::set nodes(); - - void printNodes(std::ostream & ostream); - void printEdges(std::ostream & ostream); - - std::string dotName(); - /** - * @brief returns all llvm instructions contained in the thread region - * @return - */ - std::set llvmInstructions() const; -}; - -#endif // THREADREGION_H diff --git a/include/dg/llvm/ValueRelations/GraphBuilder.h b/include/dg/llvm/ValueRelations/GraphBuilder.h deleted file mode 100644 index e0e42e957..000000000 --- a/include/dg/llvm/ValueRelations/GraphBuilder.h +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef DG_LLVM_VALUE_RELATIONS_GRAPH_BUILDER_HPP_ -#define DG_LLVM_VALUE_RELATIONS_GRAPH_BUILDER_HPP_ - -#include -#include -#include "llvm/IR/Constants.h" -#include - -#include "GraphElements.h" - -#ifndef NDEBUG -#include "getValName.h" -#endif - -namespace dg { -namespace vr { - -struct GraphBuilder { - const llvm::Module& module; - unsigned last_node_id = 0; - - // VRLocation corresponding to the state of the program BEFORE executing the instruction - std::map& locationMapping; - std::map>& blockMapping; - - GraphBuilder(const llvm::Module& m, - std::map& locs, - std::map>& blcs) - : module(m), locationMapping(locs), blockMapping(blcs) {} - - void build() { - for (const llvm::Function& f : module) { - build(f); - } - } - - void build(const llvm::Function& function) { - for (const llvm::BasicBlock& block : function) { - assert(block.size() != 0); - build(block); - } - - for (const llvm::BasicBlock& block : function) { - VRBBlock* vrblock = getVRBBlock(&block); - assert(vrblock); - - const llvm::Instruction* terminator = block.getTerminator(); - if (llvm::isa(terminator)) { - buildBranch(llvm::cast(terminator), vrblock); - - } else if (llvm::isa(terminator)) { - buildSwitch(llvm::cast(terminator), vrblock); - - } else if (llvm::isa(terminator)) { - buildReturn(llvm::cast(terminator), vrblock); - - } else if (llvm::succ_begin(&block) != llvm::succ_end(&block)) { -#ifndef NDEBUG - std::cerr << "Unhandled terminator: " << std::endl; - llvm::errs() << "Unhandled terminator: " << *terminator << "\n"; -#endif - abort(); - } - } - } - - void buildSwitch(const llvm::SwitchInst* swtch, VRBBlock* vrblock) { - for (auto& it : swtch->cases()) { - VRBBlock* succ = getVRBBlock(it.getCaseSuccessor()); - assert(succ); - - auto op = std::unique_ptr(new VRAssumeEqual(swtch->getCondition(), it.getCaseValue())); - VREdge* edge = new VREdge(vrblock->last(), succ->first(), std::move(op)); - vrblock->last()->connect(std::unique_ptr(edge)); - } - - VRBBlock* succ = getVRBBlock(swtch->getDefaultDest()); - assert(succ); - auto op = std::unique_ptr(new VRNoop()); - VREdge* edge = new VREdge(vrblock->last(), succ->first(), std::move(op)); - vrblock->last()->connect(std::unique_ptr(edge)); - - } - - void buildBranch(const llvm::BranchInst* inst, VRBBlock* vrblock) { - if (inst->isUnconditional()) { - VRBBlock* succ = getVRBBlock(inst->getSuccessor(0)); - assert(succ); - - auto op = std::unique_ptr(new VRNoop()); - VREdge* edge = new VREdge(vrblock->last(), succ->first(), std::move(op)); - - vrblock->last()->connect(std::unique_ptr(edge)); - } else { - VRBBlock* trueSucc = getVRBBlock(inst->getSuccessor(0)); - VRBBlock* falseSucc = getVRBBlock(inst->getSuccessor(1)); - - auto trueOp = std::unique_ptr(new VRAssumeBool(inst->getCondition(), true)); - auto falseOp = std::unique_ptr(new VRAssumeBool(inst->getCondition(), false)); - - VREdge* trueEdge = new VREdge(vrblock->last(), trueSucc->first(), std::move(trueOp)); - VREdge* falseEdge = new VREdge(vrblock->last(), falseSucc->first(), std::move(falseOp)); - - vrblock->last()->connect(std::unique_ptr(trueEdge)); - vrblock->last()->connect(std::unique_ptr(falseEdge)); - } - } - - void buildReturn(const llvm::ReturnInst* inst, VRBBlock* vrblock) { - auto op = std::unique_ptr(new VRInstruction(inst)); - VREdge* edge = new VREdge(vrblock->last(), nullptr, std::move(op)); - - vrblock->last()->connect(std::unique_ptr(edge)); - } - - void build(const llvm::BasicBlock& block) { - VRBBlock* vrblock = newBBlock(&block); - - auto it = block.begin(); - const llvm::Instruction* previous = &(*it); - vrblock->append(newLocation(previous)); - ++it; - - for (; it != block.end(); ++it) { - const llvm::Instruction& inst = *it; - VRLocation* newLoc = newLocation(&inst); - - VREdge* edge = new VREdge(vrblock->last(), newLoc, - std::unique_ptr(new VRInstruction(previous))); - vrblock->last()->connect(std::unique_ptr(edge)); - - vrblock->append(newLoc); - previous = &inst; - } - } - - VRLocation *newLocation(const llvm::Instruction* inst) { - assert(inst); - assert(locationMapping.find(inst) == locationMapping.end()); - - auto location = new VRLocation(++last_node_id); - assert(location); - - locationMapping.emplace(inst, location); - return location; - } - - VRBBlock *newBBlock(const llvm::BasicBlock* B) { - assert(B); - assert(blockMapping.find(B) == blockMapping.end()); - - auto block = new VRBBlock(); - assert(block); - - blockMapping.emplace(B, std::unique_ptr(block)); - return block; - } - - VRBBlock *getVRBBlock(const llvm::BasicBlock* B) { - const GraphBuilder* constThis = this; - return const_cast(constThis->getVRBBlock(B)); - } - - const VRBBlock *getVRBBlock(const llvm::BasicBlock* B) const { - auto it = blockMapping.find(B); - return it == blockMapping.end() ? nullptr : it->second.get(); - } - - VRLocation *getVRLocation(const llvm::Instruction* inst) { - const GraphBuilder* constThis = this; - return const_cast(constThis->getVRLocation(inst)); - } - - const VRLocation *getVRLocation(const llvm::Instruction* inst) const { - auto it = locationMapping.find(inst); - return it == locationMapping.end() ? nullptr : it->second; - } - -}; - -} // namespace vr -} // namespace dg - -#endif // DG_LLVM_VALUE_RELATIONS_GRAPH_BUILDER_HPP_ diff --git a/include/dg/llvm/ValueRelations/GraphElements.h b/include/dg/llvm/ValueRelations/GraphElements.h deleted file mode 100644 index 1cf543a46..000000000 --- a/include/dg/llvm/ValueRelations/GraphElements.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef DG_LLVM_VALUE_RELATIONS_GRAPH_ELEMENTS_HPP_ -#define DG_LLVM_VALUE_RELATIONS_GRAPH_ELEMENTS_HPP_ - -#include -#include - -#include "ValueRelations.h" - -#ifndef NDEBUG -#include "getValName.h" -#endif - -namespace dg { -namespace vr { - -class VROp { -protected: - enum class VROpType { NOOP, INSTRUCTION, ASSUME_BOOL, ASSUME_EQUAL } type; - VROp(VROpType t) : type(t) {} - -public: - bool isNoop() const { return type == VROpType::NOOP; } - bool isInstruction() const { return type == VROpType::INSTRUCTION; } - bool isAssume() const { return isAssumeBool() || isAssumeEqual(); } - bool isAssumeBool() const { return type == VROpType::ASSUME_BOOL; } - bool isAssumeEqual() const { return type == VROpType::ASSUME_EQUAL; } - - virtual ~VROp() = default; - -#ifndef NDEBUG - virtual std::string toStr() const = 0; - - void generalDump(std::ostream& stream) { - stream << toStr(); - } - - void dump() { - generalDump(std::cout); - } -#endif -}; - -struct VRNoop : public VROp { - VRNoop() : VROp(VROpType::NOOP) {} - -#ifndef NDEBUG - std::string toStr() const override { - return "(noop)"; - } -#endif -}; - -struct VRInstruction : public VROp { - const llvm::Instruction* instruction; - - VRInstruction(const llvm::Instruction* I) - : VROp(VROpType::INSTRUCTION), instruction(I) {} - - const llvm::Instruction* getInstruction() const { return instruction; } - -#ifndef NDEBUG - std::string toStr() const override { - return debug::getValName(instruction); - } -#endif -}; - -struct VRAssume : public VROp { - const llvm::Value* val; - - const llvm::Value* getValue() const { - return val; - } - -protected: - VRAssume(VROpType type, const llvm::Value* v) : VROp(type), val(v) {} - -#ifndef NDEBUG - std::string toStr() const override { - return "assuming " + debug::getValName(val) + " is "; - } -#endif -}; - -struct VRAssumeBool : public VRAssume { - bool assumption; - - VRAssumeBool(const llvm::Value* v, bool b) - : VRAssume(VROpType::ASSUME_BOOL, v), assumption(b) {} - - bool getAssumption() const { - return assumption; - } - -#ifndef NDEBUG - std::string toStr() const override { - return VRAssume::toStr() + (assumption ? "true" : "false"); - } -#endif -}; - -struct VRAssumeEqual : public VRAssume { - const llvm::Value* assumption; - - VRAssumeEqual(const llvm::Value* v, const llvm::Value* a) - : VRAssume(VROpType::ASSUME_EQUAL, v), assumption(a) {} - - const llvm::Value* getAssumption() const { - return assumption; - } - -#ifndef NDEBUG - std::string toStr() const override { - return VRAssume::toStr() + debug::getValName(assumption); - } -#endif -}; - -struct VRLocation; - -enum EdgeType { - TREE, - BACK, - FORWARD, - DEFAULT - // DANGER ignores cross -}; - -struct VREdge { - VRLocation *source; - VRLocation *target; - - std::unique_ptr op; - - EdgeType type = EdgeType::DEFAULT; - - VREdge(VRLocation *s, VRLocation *t, std::unique_ptr&& op) - : source(s), target(t), op(std::move(op)) {} -}; - -struct VRLocation { - const unsigned id; - - bool inLoop = false; - - ValueRelations relations; - - std::vector predecessors; - std::vector> successors; - - VRLocation(unsigned _id) : id(_id) {} - - void connect(std::unique_ptr&& edge) { - if (edge->target) - edge->target->predecessors.push_back(edge.get()); - successors.emplace_back(std::move(edge)); - } - - std::vector getPredecessors() { - return predecessors; - } - - std::vector getSuccessors() { // TODO create an iterator to unwrap the unique pointers - std::vector result; - for (auto& succ : successors) { - result.push_back(succ.get()); - } - return result; - } - - std::vector getPredLocations() { - std::vector result; - for (VREdge * edge : predecessors) { - result.push_back(edge->source); - } - return result; - } - - std::vector getSuccLocations() { - std::vector result; - for (auto& edge : successors) { - result.push_back(edge->target); - } - return result; - } - - bool isJoin() const { - return predecessors.size() > 1; - } - - bool isJustBranchJoin() const { - // allows TREE and FORWARD - if (!isJoin()) return false; - for (VREdge* pred : predecessors) { - if (pred->type == EdgeType::BACK) - return false; - } - return true; - } - - bool isJustLoopJoin() const { - // allows TREE and BACK - if (!isJoin()) return false; - for (VREdge* pred : predecessors) { - if (pred->type == EdgeType::FORWARD) - return false; - } - return true; - } - -#ifndef NDEBUG - void dump() const { - std::cout << id << std::endl; - } -#endif -}; - -struct VRBBlock { - std::list> locations; - - void prepend(VRLocation* loc) { - locations.emplace(locations.begin(), loc); - } - - void append(VRLocation* loc) { - locations.emplace_back(loc); - } - - VRLocation *last() { return locations.back().get(); } - VRLocation *first() { return locations.front().get(); } - const VRLocation *last() const { return locations.back().get(); } - const VRLocation *first() const { return locations.front().get(); } - - auto begin() -> decltype(locations.begin()) { return locations.begin(); } - auto end() -> decltype(locations.end()) { return locations.end(); } - auto begin() const -> decltype(locations.begin()) { return locations.begin(); } - auto end() const -> decltype(locations.end()) { return locations.end(); } -}; - -} // namespace vr -} // namespace dg - -#endif //DG_LLVM_VALUE_RELATIONS_GRAPH_ELEMENTS_HPP_ diff --git a/include/dg/llvm/ValueRelations/RelationsAnalyzer.h b/include/dg/llvm/ValueRelations/RelationsAnalyzer.h deleted file mode 100644 index f84d31c02..000000000 --- a/include/dg/llvm/ValueRelations/RelationsAnalyzer.h +++ /dev/null @@ -1,1153 +0,0 @@ -#ifndef DG_LLVM_VALUE_RELATION_RELATIONS_ANALYZER_HPP_ -#define DG_LLVM_VALUE_RELATION_RELATIONS_ANALYZER_HPP_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include -#include - -#include "GraphElements.h" -#include "ValueRelations.h" -#include "StructureAnalyzer.h" - -#ifndef NDEBUG -#include "getValName.h" -#endif - -namespace dg { -namespace vr { - - -class RelationsAnalyzer { - - const std::set safeFunctions = { "__VERIFIER_nondet_int", "__VERIFIER_nondet_char" }; - - const llvm::Module& module; - - // VRLocation corresponding to the state of the program BEFORE executing the instruction - const std::map& locationMapping; - const std::map>& blockMapping; - - // holds information about structural properties of analyzed module - // like set of instructions executed in loop starging at given location - // or possibly set of values defined at given location - const StructureAnalyzer& structure; - - bool processOperation(VRLocation* source, VRLocation* target, VROp* op) { - if (!target) return false; - assert(source && target && op); - - ValueRelations newGraph = source->relations; - - if (op->isInstruction()) { - const llvm::Instruction* inst = static_cast(op)->getInstruction(); - forgetInvalidated(newGraph, inst); - processInstruction(newGraph, inst); - } else if (op->isAssume()) { - if (op->isAssumeBool()) - processAssumeBool(newGraph, static_cast(op)); - else // isAssumeEqual - processAssumeEqual(newGraph, static_cast(op)); - } // else op is noop - - return andSwapIfChanged(target->relations, newGraph); - } - - void forgetInvalidated(ValueRelations& graph, const llvm::Instruction* inst) const { - - for (const llvm::Value* invalid : instructionInvalidatesFromGraph(graph, inst)) - graph.unsetAllLoadsByPtr(invalid); - } - - void addAndUnwrapLoads( - std::set>& writtenTo, - const llvm::Value* val) const { - - unsigned depth = 0; - writtenTo.emplace(val, 0); - while (auto load = llvm::dyn_cast(val)) { - writtenTo.emplace(load->getPointerOperand(), ++depth); - val = load->getPointerOperand(); - } - } - - std::set> instructionInvalidates(const llvm::Instruction* inst) const { - - if (!inst->mayWriteToMemory() && !inst->mayHaveSideEffects()) - return std::set>(); - - if (auto intrinsic = llvm::dyn_cast(inst)) { - if (isIgnorableIntrinsic(intrinsic->getIntrinsicID())) { - return std::set>(); - } - } - - if (auto call = llvm::dyn_cast(inst)) { - auto function = call->getCalledFunction(); - if (function && safeFunctions.find(function->getName().str()) != safeFunctions.end()) - return std::set>(); - } - - std::set> unsetAll = { {nullptr, 0} }; - - auto store = llvm::dyn_cast(inst); - if (!store) // most probably CallInst - // unable to presume anything about such instruction - return unsetAll; - - // if store writes to a fix location, it cannot be easily said which - // values it affects - if (llvm::isa(store->getPointerOperand())) - return unsetAll; - - const llvm::Value* memoryPtr = store->getPointerOperand(); - const llvm::Value* underlyingPtr = stripCastsAndGEPs(memoryPtr); - - std::set> writtenTo; - // DANGER TODO unset everything in between too - addAndUnwrapLoads(writtenTo, underlyingPtr); // unset underlying memory - addAndUnwrapLoads(writtenTo, memoryPtr); // unset pointer itself - - const ValueRelations& graph = locationMapping.at(store)->relations; - - // every pointer with unknown origin is considered having an alias - if (mayHaveAlias(graph, memoryPtr) || !hasKnownOrigin(graph, memoryPtr)) { - - // if invalidated memory may have an alias, unset all memory whose - // origin is unknown since it may be the alias - for (const auto& fromsValues : graph.getAllLoads()) { - if (!hasKnownOrigin(graph, fromsValues.first[0])) { - addAndUnwrapLoads(writtenTo, fromsValues.first[0]); - } - } - } - - if (!hasKnownOrigin(graph, memoryPtr)) { - - // if memory does not have a known origin, unset all values which - // may have an alias, since this memory may be the alias - for (const auto& fromsValues : graph.getAllLoads()) { - if (mayHaveAlias(graph, fromsValues.first[0])) - addAndUnwrapLoads(writtenTo, fromsValues.first[0]); - } - } - - return writtenTo; - } - - const llvm::Value* getInvalidatedPointer( - const ValueRelations& graph, - const llvm::Value* invalid, - unsigned depth) const { - - while (depth && invalid) { - const auto& values = graph.getValsByPtr(invalid); - - if (values.empty()) { - invalid = nullptr; // invalidated pointer does not load anything in current graph - } else { - invalid = values[0]; - --depth; - } - } - return graph.hasLoad(invalid) ? invalid : nullptr; - } - - // returns set of values that have a load in given graph and are invalidated - // by the instruction - std::set instructionInvalidatesFromGraph( - const ValueRelations& graph, - const llvm::Instruction* inst) const { - - const auto& indirectlyInvalid = instructionInvalidates(inst); - - // go through all (indireclty) invalidated pointers and add those - // that occur in current location - std::set allInvalid; - for (const auto& pair : indirectlyInvalid) { - if (!pair.first) { - // add all loads in graph - for (auto& fromsValues : graph.getAllLoads()) - allInvalid.emplace(fromsValues.first[0]); - break; - } - - auto directlyInvalid = getInvalidatedPointer(graph, pair.first, pair.second); - if (directlyInvalid) - allInvalid.emplace(directlyInvalid); - } - return allInvalid; - } - - bool mayHaveAlias(const ValueRelations& graph, const llvm::Value* val) const { - for (auto eqval : graph.getEqual(val)) - if (mayHaveAlias(llvm::cast(eqval))) - return true; - return false; - } - - bool mayHaveAlias(const llvm::User* val) const { - // if value is not pointer, we don't care whether there can be other name for same value - if (!val->getType()->isPointerTy()) - return false; - - for (const llvm::User* user : val->users()) { - - // if value is stored, it can be accessed - if (llvm::isa(user)) { - if (user->getOperand(0) == val) - return true; - - } else if (llvm::isa(user)) { - if (mayHaveAlias(user)) - return true; - - } else if (auto gep = llvm::dyn_cast(user)) { - if (gep->getPointerOperand() == val) { - if (gep->hasAllZeroIndices()) - return true; - - // TODO really? remove - llvm::Type* valType = val->getType(); - llvm::Type* gepType = gep->getPointerOperandType(); - if (gepType->isVectorTy() || valType->isVectorTy()) - assert(0 && "i dont know what it is and when does it happen"); - if (gepType->getPrimitiveSizeInBits() < valType->getPrimitiveSizeInBits()) - return true; - } - - } else if (auto intrinsic = llvm::dyn_cast(user)) { - if (!isIgnorableIntrinsic(intrinsic->getIntrinsicID()) && intrinsic->mayWriteToMemory()) - return true; - - } else if (auto inst = llvm::dyn_cast(user)) { - if (inst->mayWriteToMemory()) - return true; - } - } - return false; - } - - bool isIgnorableIntrinsic(llvm::Intrinsic::ID id) const { - switch(id) { - case llvm::Intrinsic::lifetime_start: - case llvm::Intrinsic::lifetime_end: - case llvm::Intrinsic::stacksave: - case llvm::Intrinsic::stackrestore: - case llvm::Intrinsic::dbg_declare: - case llvm::Intrinsic::dbg_value: - return true; - default: - return false; - } - } - - static const llvm::Value* stripCastsAndGEPs(const llvm::Value* memoryPtr) { - memoryPtr = memoryPtr->stripPointerCasts(); - while (auto gep = llvm::dyn_cast(memoryPtr)) { - memoryPtr = gep->getPointerOperand()->stripPointerCasts(); - } - return memoryPtr; - } - - static bool hasKnownOrigin(const ValueRelations& graph, const llvm::Value* from) { - for (auto memoryPtr : graph.getEqual(from)) { - memoryPtr = stripCastsAndGEPs(memoryPtr); - if (llvm::isa(memoryPtr)) - return true; - } - return false; - } - - void storeGen(ValueRelations& graph, const llvm::StoreInst* store) { - graph.setLoad(store->getPointerOperand()->stripPointerCasts(), store->getValueOperand()); - } - - void loadGen(ValueRelations& graph, const llvm::LoadInst* load) { - graph.setLoad(load->getPointerOperand()->stripPointerCasts(), load); - } - - void gepGen(ValueRelations& graph, const llvm::GetElementPtrInst* gep) { - if (gep->hasAllZeroIndices()) - graph.setEqual(gep, gep->getPointerOperand()); - - for (auto& fromsValues : graph.getAllLoads()) { - for (const llvm::Value* from : fromsValues.first) { - if (auto otherGep = llvm::dyn_cast(from)) { - if (operandsEqual(graph, gep, otherGep, true)) - graph.setEqual(gep, otherGep); - } - } - } - // TODO something more? - // indices method gives iterator over indices - } - - void extGen(ValueRelations& graph, const llvm::CastInst* ext) { - graph.setEqual(ext, ext->getOperand(0)); - } - - void addGen(ValueRelations& graph, const llvm::BinaryOperator* add) { - auto c1 = llvm::dyn_cast(add->getOperand(0)); - auto c2 = llvm::dyn_cast(add->getOperand(1)); - // TODO check wheter equal to constant - - solveEquality(graph, add); - solveCommutativity(graph, add); - - if (solvesSameType(graph, c1, c2, add)) - return; - - const llvm::Value* param = nullptr; - if (c2) { - c1 = c2; - param = add->getOperand(0); - } - else - param = add->getOperand(1); - - assert(c1 && add && param); - // add = param + c1 - if (c1->isZero()) - return graph.setEqual(add, param); - - else if (c1->isNegative()) { - // c1 < 0 ==> param + c1 < param - graph.setLesser(add, param); - - // lesser < param ==> lesser <= param + (-1) - if (c1->isMinusOne()) - solvesDiffOne(graph, param, add, true); - - } else { - // c1 > 0 => param < param + c1 - graph.setLesser(param, add); - - // param < greater => param + 1 <= greater - if (c1->isOne()) - solvesDiffOne(graph, param, add, false); - } - - const llvm::ConstantInt* constBound = graph.getLesserEqualBound(param); - if (constBound) { - const llvm::APInt& boundResult = constBound->getValue() + c1->getValue(); - const llvm::Constant* llvmResult = llvm::ConstantInt::get(add->getType(), boundResult); - if (graph.isLesser(constBound, param)) - graph.setLesser(llvmResult, add); - else if (graph.isEqual(constBound, param)) - graph.setEqual(llvmResult, add); - else - graph.setLesserEqual(llvmResult, add); - } - } - - void subGen(ValueRelations& graph, const llvm::BinaryOperator* sub) { - auto c1 = llvm::dyn_cast(sub->getOperand(0)); - auto c2 = llvm::dyn_cast(sub->getOperand(1)); - // TODO check wheter equal to constant - - solveEquality(graph, sub); - - if (solvesSameType(graph, c1, c2, sub)) - return; - - if (c1) { - // TODO collect something here? - return; - } - - const llvm::Value* param = sub->getOperand(0); - assert(c2 && sub && param); - // sub = param - c1 - if (c2->isZero()) - return graph.setEqual(sub, param); - - else if (c2->isNegative()) { - // c1 < 0 ==> param < param - c1 - graph.setLesser(param, sub); - - // param < greater ==> param - (-1) <= greater - if (c2->isMinusOne()) - solvesDiffOne(graph, param, sub, false); - - } else { - // c1 > 0 => param - c1 < param - graph.setLesser(sub, param); - - // lesser < param ==> lesser <= param - 1 - if (c2->isOne()) - solvesDiffOne(graph, param, sub, true); - } - - const llvm::ConstantInt* constBound = graph.getLesserEqualBound(param); - if (constBound) { - const llvm::APInt& boundResult = constBound->getValue() - c2->getValue(); - const llvm::Constant* llvmResult = llvm::ConstantInt::get(sub->getType(), boundResult); - - if (graph.isLesser(constBound, param)) - graph.setLesser(llvmResult, sub); - else if (graph.isEqual(constBound, param)) - graph.setEqual(llvmResult, sub); - else - graph.setLesserEqual(llvmResult, sub); - } - } - - void mulGen(ValueRelations& graph, const llvm::BinaryOperator* mul) { - auto c1 = llvm::dyn_cast(mul->getOperand(0)); - auto c2 = llvm::dyn_cast(mul->getOperand(1)); - // TODO check wheter equal to constant - - solveEquality(graph, mul); - solveCommutativity(graph, mul); - - if (solvesSameType(graph, c1, c2, mul)) - return; - - const llvm::Value* param = nullptr; - if (c2) { - c1 = c2; - param = mul->getOperand(0); - } - else - param = mul->getOperand(1); - - assert(c1 && mul && param); - // mul = param + c1 - if (c1->isZero()) - return graph.setEqual(mul, c1); - else if (c1->isOne()) - return graph.setEqual(mul, param); - - // TODO collect something here? - } - - bool solvesSameType(ValueRelations& graph, - const llvm::ConstantInt* c1, const llvm::ConstantInt* c2, - const llvm::BinaryOperator* op) { - if (c1 && c2) { - llvm::APInt result; - - switch(op->getOpcode()) { - case llvm::Instruction::Add: - result = c1->getValue() + c2->getValue(); - break; - case llvm::Instruction::Sub: - result = c1->getValue() - c2->getValue(); - break; - case llvm::Instruction::Mul: - result = c1->getValue() * c2->getValue(); - break; - default: - assert(0 && "solvesSameType: shouldn't handle any other operation"); - } - graph.setEqual(op, llvm::ConstantInt::get(c1->getType(), result)); - return true; - } - - llvm::Type* i32 = llvm::Type::getInt32Ty(op->getContext()); - const llvm::Constant* one = llvm::ConstantInt::getSigned(i32, 1); - const llvm::Constant* minusOne = llvm::ConstantInt::getSigned(i32, -1); - - const llvm::Value* fst = op->getOperand(0); - const llvm::Value* snd = op->getOperand(1); - - if (!c1 && !c2) { - switch (op->getOpcode()) { - case llvm::Instruction::Add: - if (graph.isLesserEqual(one, fst)) graph.setLesser(snd, op); - if (graph.isLesserEqual(one, snd)) graph.setLesser(fst, op); - if (graph.isLesserEqual(fst, minusOne)) graph.setLesser(op, snd); - if (graph.isLesserEqual(snd, minusOne)) graph.setLesser(op, fst); - break; - case llvm::Instruction::Sub: - if (graph.isLesserEqual(one, snd)) graph.setLesser(op, fst); - if (graph.isLesserEqual(snd, minusOne)) graph.setLesser(fst, op); - break; - default: - break; - } - return true; - } - return false; - } - - void solvesDiffOne(ValueRelations& graph, - const llvm::Value* param, - const llvm::BinaryOperator* op, - bool getLesser) { - - std::vector sample = getLesser ? - graph.getDirectlyLesser(param) : graph.getDirectlyGreater(param); - - for (const llvm::Value* value : sample) - if (getLesser) - graph.setLesserEqual(value, op); - else - graph.setLesserEqual(op, value); - } - - bool operandsEqual(ValueRelations& graph, - const llvm::Instruction* fst, - const llvm::Instruction* snd, - bool sameOrder) const { // false means checking in reverse order - unsigned total = fst->getNumOperands(); - if (total != snd->getNumOperands()) - return false; - - for (unsigned i = 0; i < total; ++i) { - unsigned otherI = sameOrder ? i : total - i - 1; - - if (!graph.isEqual(fst->getOperand(i), snd->getOperand(otherI))) - return false; - } - return true; - } - - void solveByOperands(ValueRelations& graph, const llvm::BinaryOperator* operation, bool sameOrder) { - for (auto same : structure.getInstructionSetFor(operation->getOpcode())) { - auto sameOperation = llvm::dyn_cast(same); - - if (operandsEqual(graph, operation, sameOperation, sameOrder)) - graph.setEqual(operation, sameOperation); - } - } - - void solveEquality(ValueRelations& graph, const llvm::BinaryOperator* operation) { - solveByOperands(graph, operation, true); - } - - void solveCommutativity(ValueRelations& graph, const llvm::BinaryOperator* operation) { - solveByOperands(graph, operation, false); - } - - void remGen(ValueRelations& graph, const llvm::BinaryOperator* rem) { - assert(rem); - const llvm::Constant* zero = llvm::ConstantInt::getSigned(rem->getType(), 0); - - if (!graph.isLesserEqual(zero, rem->getOperand(0))) - return; - - graph.setLesserEqual(zero, rem); - graph.setLesser(rem, rem->getOperand(1)); - } - - void castGen(ValueRelations& graph, const llvm::CastInst* cast) { - if (cast->isLosslessCast() || cast->isNoopCast(module.getDataLayout())) - graph.setEqual(cast, cast->getOperand(0)); - } - - void processInstruction(ValueRelations& graph, const llvm::Instruction* inst) { - switch(inst->getOpcode()) { - case llvm::Instruction::Store: - return storeGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::Load: - return loadGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::GetElementPtr: - return gepGen(graph, llvm::cast(inst)); - case llvm::Instruction::ZExt: - case llvm::Instruction::SExt: // (S)ZExt should not change value - return extGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::Add: - return addGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::Sub: - return subGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::Mul: - return mulGen(graph, llvm::dyn_cast(inst)); - case llvm::Instruction::SRem: - case llvm::Instruction::URem: - return remGen(graph, llvm::dyn_cast(inst)); - default: - if (auto cast = llvm::dyn_cast(inst)) { - return castGen(graph, cast); - } - } - } - - void processAssumeBool(ValueRelations& newGraph, VRAssumeBool* assume) const { - if (llvm::isa(assume->getValue())) - processICMP(newGraph, assume); - if (llvm::isa(assume->getValue())) - processPhi(newGraph, assume); - } - - void processICMP(ValueRelations& newGraph, VRAssumeBool* assume) const { - const llvm::ICmpInst* icmp = llvm::cast(assume->getValue()); - bool assumption = assume->getAssumption(); - - const llvm::Value* op1 = icmp->getOperand(0); - const llvm::Value* op2 = icmp->getOperand(1); - - llvm::ICmpInst::Predicate pred = assumption ? - icmp->getSignedPredicate() : icmp->getInversePredicate(); - - switch (pred) { - case llvm::ICmpInst::Predicate::ICMP_EQ: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::EQ)) { - newGraph.setEqual(op1, op2); - return; - } - break; - - case llvm::ICmpInst::Predicate::ICMP_NE: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::NE)) { - newGraph.setNonEqual(op1, op2); - return; - } - break; - - case llvm::ICmpInst::Predicate::ICMP_ULE: - case llvm::ICmpInst::Predicate::ICMP_SLE: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::LE)) { - newGraph.setLesserEqual(op1, op2); - return; - } - break; - - case llvm::ICmpInst::Predicate::ICMP_ULT: - case llvm::ICmpInst::Predicate::ICMP_SLT: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::LT)) { - newGraph.setLesser(op1, op2); - return; - } - break; - - case llvm::ICmpInst::Predicate::ICMP_UGE: - case llvm::ICmpInst::Predicate::ICMP_SGE: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::GE)) { - newGraph.setLesserEqual(op2, op1); - return; - } - break; - - case llvm::ICmpInst::Predicate::ICMP_UGT: - case llvm::ICmpInst::Predicate::ICMP_SGT: - if (!newGraph.hasConflictingRelation(op1, op2, Relation::GT)) { - newGraph.setLesser(op2, op1); - return; - } - break; - - default: - #ifndef NDEBUG - llvm::errs() << "Unhandled predicate in" << *icmp << "\n"; - #endif - abort(); - } - - // reachable only if conflicting relation found - newGraph.unsetComparativeRelations(op1); - newGraph.unsetComparativeRelations(op2); - } - - void processPhi(ValueRelations& newGraph, VRAssumeBool* assume) const { - const llvm::PHINode* phi = llvm::cast(assume->getValue()); - bool assumption = assume->getAssumption(); - - const llvm::BasicBlock* assumedPred = nullptr; - for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) { - const llvm::Value* result = phi->getIncomingValue(i); - auto constResult = llvm::dyn_cast(result); - if (!constResult || (constResult - && ((constResult->isOne() && assumption) - || (constResult->isZero() && !assumption)))) { - if (!assumedPred) - assumedPred = phi->getIncomingBlock(i); - else - return; // we found other viable incoming block - } - } - assert (assumedPred); - - VRBBlock* vrbblock = blockMapping.at(assumedPred).get(); - VRLocation* source = vrbblock->last(); - bool result = newGraph.merge(source->relations); - assert(result); - } - - void processAssumeEqual(ValueRelations& newGraph, VRAssumeEqual* assume) const { - const llvm::Value* val1 = assume->getValue(); - const llvm::Value* val2 = assume->getAssumption(); - if (!newGraph.hasConflictingRelation(val1, val2, Relation::EQ)) - newGraph.setEqual(val1, val2); - } - - bool relatesInAll(const std::vector& locations, - const llvm::Value* fst, - const llvm::Value* snd, - bool (ValueRelations::*relates)(const llvm::Value*, const llvm::Value*) const) const { - // which is function pointer to isEqual, isLesser, or isLesserEqual - - for (const VRLocation* vrloc : locations) { - if (!(vrloc->relations.*relates)(fst, snd)) - return false; - } - return true; - } - - bool relatesByLoadInAll(const std::vector& locations, - const llvm::Value* related, - const llvm::Value* from, - bool (ValueRelations::*relates)(const llvm::Value*, const llvm::Value*) const, - bool flip) const { - - for (const VRLocation* vrloc : locations) { - const std::vector& loaded = vrloc->relations.getValsByPtr(from); - if (loaded.empty() - || (!flip && !(vrloc->relations.*relates)(related, loaded[0])) - || ( flip && !(vrloc->relations.*relates)(loaded[0], related))) - return false; - } - return true; - } - - bool mergeRelations(VRLocation* location) { - return mergeRelations(location->getPredLocations(), location); - } - - bool mergeRelations(const std::vector& preds, VRLocation* location) { - if (preds.empty()) - return false; - - ValueRelations newGraph = location->relations; - ValueRelations& oldGraph = preds[0]->relations; - std::vector values = oldGraph.getAllValues(); - - // merge from all predecessors - for (auto valueIt = values.begin(); valueIt != values.end(); ++valueIt) { - const llvm::Value* val = *valueIt; - - for (auto it = oldGraph.begin_lesserEqual(val); - it != oldGraph.end_lesserEqual(val); - ++it) { - const llvm::Value* related; Relation relation; - std::tie(related, relation) = *it; - - if (related == val) - continue; - - switch (relation) { - case Relation::EQ: - if (relatesInAll(preds, related, val, &ValueRelations::isEqual)) { - newGraph.setEqual(related, val); - - auto found = std::find(values.begin(), values.end(), related); - if (found != values.end()) { - values.erase(found); - valueIt = std::find(values.begin(), values.end(), val); - } - } - break; - - case Relation::LT: - if (relatesInAll(preds, related, val, &ValueRelations::isLesser)) - newGraph.setLesser(related, val); - break; - - case Relation::LE: - if (relatesInAll(preds, related, val, &ValueRelations::isLesserEqual)) - newGraph.setLesserEqual(related, val); - break; - - default: assert(0 && "going down, not up"); - } - } - } - - // merge relations from tree predecessor only - VRLocation* treePred = getTreePred(location); - const ValueRelations& treePredGraph = treePred->relations; - - if (location->isJustLoopJoin()) { - bool result = newGraph.merge(treePredGraph, true); - assert(result); - } - - return andSwapIfChanged(location->relations, newGraph); - } - - bool loadsInAll(const std::vector& locations, const llvm::Value* from, const llvm::Value* value) const { - for (const VRLocation* vrloc : locations) { - if (!vrloc->relations.isLoad(from, value)) - // DANGER does it suffice that from equals to value's ptr (before instruction on edge)? - return false; - } - return true; - } - - bool loadsSomethingInAll(const std::vector& locations, const llvm::Value* from) const { - for (const VRLocation* vrloc : locations) { - if (!vrloc->relations.hasLoad(from)) - return false; - } - return true; - } - - bool mergeLoads(VRLocation* location) { - return mergeLoads(location->getPredLocations(), location); - } - - bool mergeLoads(const std::vector& preds, VRLocation* location) { - if (preds.empty()) - return false; - - ValueRelations newGraph = location->relations; - const auto& loadBucketPairs = preds[0]->relations.getAllLoads(); - - // merge loads from all predecessors - for (const auto& fromsValues : loadBucketPairs) { - for (const llvm::Value* from : fromsValues.first) { - for (const llvm::Value* val : fromsValues.second) { - if (loadsInAll(preds, from, val)) - newGraph.setLoad(from, val); - } - } - } - - // merge loads from outloop predecessor, that are not invalidated - // inside the loop - if (location->isJustLoopJoin()) { - - const ValueRelations& oldGraph = getTreePred(location)->relations; - - std::set allInvalid; - - for (const auto* inst : structure.getInloopValues(location)) { - auto invalid = instructionInvalidatesFromGraph(oldGraph, inst); - allInvalid.insert(invalid.begin(), invalid.end()); - } - - for (const auto& fromsValues : oldGraph.getAllLoads()) { - if (!anyInvalidated(allInvalid, fromsValues.first)) { - for (auto from : fromsValues.first) { - for (auto val : fromsValues.second) { - newGraph.setLoad(from, val); - } - } - } - } - } - - return andSwapIfChanged(location->relations, newGraph); - } - - VRLocation* getTreePred(VRLocation* location) const { - VRLocation* treePred = nullptr; - for (VREdge* predEdge : location->predecessors) { - if (predEdge->type == EdgeType::TREE) - treePred = predEdge->source; - } - assert(treePred); - return treePred; - } - - bool hasConflictLoad(const std::vector& preds, - const llvm::Value* from, - const llvm::Value* val) { - for (const VRLocation* pred : preds) { - for (const auto& fromsValues : pred->relations.getAllLoads()) { - auto findFrom = std::find(fromsValues.first.begin(), fromsValues.first.end(), from); - auto findVal = std::find(fromsValues.second.begin(), fromsValues.second.end(), val); - - if (findFrom != fromsValues.first.end() && findVal == fromsValues.second.end()) - return true; - } - } - return false; - } - - bool anyInvalidated(const std::set& allInvalid, - const std::vector& froms) { - for (auto from : froms) { - if (allInvalid.find(from) != allInvalid.end()) - return true; - } - return false; - } - - bool mergeRelationsByLoads(VRLocation* location) { - return mergeRelationsByLoads(location->getPredLocations(), location); - } - - bool mergeRelationsByLoads(const std::vector& preds, VRLocation* location) { - ValueRelations newGraph = location->relations; - - std::vector froms; - for (auto fromsValues : preds[0]->relations.getAllLoads()) { - for (auto from : fromsValues.first) { - if (isGoodFromForPlaceholder(preds, from, fromsValues.second)) - froms.emplace_back(from); - } - } - - // infer some invariants in loop - if (preds.size() == 2 && location->isJustLoopJoin() - && preds[0]->relations.holdsAnyRelations() - && preds[1]->relations.holdsAnyRelations()) - inferChangeInLoop(newGraph, froms, location); - - inferFromChangeLocations(newGraph, location); - - return andSwapIfChanged(location->relations, newGraph); - } - - void inferChangeInLoop(ValueRelations& newGraph, - const std::vector& froms, - VRLocation* location) { - for (const llvm::Value* from : froms) { - const auto& predEdges = location->predecessors; - - VRLocation* outloopPred = predEdges[0]->type == EdgeType::BACK ? - predEdges[1]->source : predEdges[0]->source; - VRLocation* inloopPred = predEdges[0]->type == EdgeType::BACK ? - predEdges[0]->source : predEdges[1]->source; - - std::vector valsInloop - = inloopPred->relations.getValsByPtr(from); - if (valsInloop.empty()) - continue; - const llvm::Value* valInloop = valsInloop[0]; - - std::vector allRelated - = inloopPred->relations.getAllRelated(valInloop); - - // get some value, that is both related to the value loaded from - // from at the end of the loop and at the same time is loaded - // from from in given loop - const llvm::Value* firstLoadInLoop = nullptr; - for (const auto* val : structure.getInloopValues(location)) { - - const ValueRelations& relations = locationMapping.at(val)->relations; - auto invalidated = instructionInvalidatesFromGraph(relations, val); - if (invalidated.find(from) != invalidated.end()) - break; - - if (std::find(allRelated.begin(), allRelated.end(), val) - != allRelated.end()) { - if (auto load = llvm::dyn_cast(val)) { - if (load->getPointerOperand() == from) { - firstLoadInLoop = load; - break; - } - } - } - } - - // set all preserved relations - if (firstLoadInLoop) { - - // get all equal vals from load from outloopPred - std::vector valsOutloop - = outloopPred->relations.getValsByPtr(from); - if (valsOutloop.empty()) - continue; - - unsigned placeholder = newGraph.newPlaceholderBucket(); - - if (inloopPred->relations.isLesser(firstLoadInLoop, valInloop)) - newGraph.setLesserEqual(valsOutloop[0], placeholder); - - if (inloopPred->relations.isLesser(valInloop, firstLoadInLoop)) - newGraph.setLesserEqual(placeholder, valsOutloop[0]); - - if (newGraph.hasComparativeRelations(placeholder)) { - newGraph.setLoad(from, placeholder); - - for (const llvm::Value* val : valsOutloop) { - newGraph.setEqual(valsOutloop[0], val); - } - } else { - newGraph.erasePlaceholderBucket(placeholder); - } - } - } - } - - void inferFromChangeLocations(ValueRelations& newGraph, VRLocation* location) { - if (location->isJustLoopJoin()) { - VRLocation* treePred = getTreePred(location); - - for (auto fromsValues : treePred->relations.getAllLoads()) { - for (const llvm::Value* from : fromsValues.first) { - std::vector locationsAfterInvalidating = { treePred }; - - // get all locations which influence value loaded from from - for (const llvm::Instruction* invalidating : structure.getInloopValues(location)) { - const ValueRelations& relations = locationMapping.at(invalidating)->relations; - auto invalidated = instructionInvalidatesFromGraph(relations, invalidating); - - if (invalidated.find(from) != invalidated.end()) { - locationsAfterInvalidating.emplace_back(locationMapping.at(invalidating)->getSuccLocations()[0]); - } - } - - if (!isGoodFromForPlaceholder(locationsAfterInvalidating, from, fromsValues.second)) - continue; - - intersectByLoad(locationsAfterInvalidating, from, newGraph); - } - } - } - } - - bool isGoodFromForPlaceholder(const std::vector& preds, const llvm::Value* from, const std::vector values) { - if (!loadsSomethingInAll(preds, from)) - return false; - - for (auto value : values) { - if (loadsInAll(preds, from, value)) - return false; - } - return true; - } - - void intersectByLoad(const std::vector& preds, const llvm::Value* from, ValueRelations& newGraph) { - auto& loads = preds[0]->relations.getValsByPtr(from); - if (loads.empty()) - return; - - const llvm::ConstantInt* bound = nullptr; - for (VRLocation* pred : preds) { - const ValueRelations& predGraph = pred->relations; - - auto& loads = predGraph.getValsByPtr(from); - if (loads.empty()) - return; - - const llvm::ConstantInt* value = predGraph.getLesserEqualBound(loads[0]); - if (!value) { - bound = nullptr; - break; - } - - if (!bound || value->getValue().slt(bound->getValue())) - bound = value; - } - - unsigned placeholder = newGraph.newPlaceholderBucket(); - - if (bound) - newGraph.setLesserEqual(bound, placeholder); - - const llvm::Value* loaded = preds[0]->relations.getValsByPtr(from)[0]; - - for (auto it = preds[0]->relations.begin_all(loaded); - it != preds[0]->relations.end_all(loaded); - ++it) { - const llvm::Value* related; Relation relation; - std::tie(related, relation) = *it; - - if (related == loaded) - continue; - - switch (relation) { - case Relation::EQ: - if (relatesByLoadInAll(preds, related, from, &ValueRelations::isEqual, false)) - newGraph.setEqual(related, placeholder); - - else if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, false)) - newGraph.setLesserEqual(related, placeholder); - - else if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, true)) - newGraph.setLesserEqual(placeholder, related); - - break; - - case Relation::LT: - if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesser, false)) - newGraph.setLesser(related, placeholder); - - else if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, false)) - newGraph.setLesserEqual(related, placeholder); - - break; - - case Relation::LE: - if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, false)) - newGraph.setLesserEqual(related, placeholder); - - break; - - case Relation::GT: - if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesser, true)) - newGraph.setLesser(placeholder, related); - - else if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, true)) - newGraph.setLesserEqual(placeholder, related); - - break; - - case Relation::GE: - if (relatesByLoadInAll(preds, related, from, &ValueRelations::isLesserEqual, true)) - newGraph.setLesserEqual(placeholder, related); - - break; - - default: - assert(0 && "other relations do not participate"); - } - } - - if (newGraph.hasComparativeRelations(placeholder)) - newGraph.setLoad(from, placeholder); - else - newGraph.erasePlaceholderBucket(placeholder); - } - - bool andSwapIfChanged(ValueRelations& oldGraph, ValueRelations& newGraph) { - if (oldGraph.hasAllRelationsFrom(newGraph) && newGraph.hasAllRelationsFrom(oldGraph)) - return false; - - swap(oldGraph, newGraph); - return true; - } - - bool analysisPass() { - bool changed = false; - - for (auto& pair : blockMapping) { - auto& vrblockPtr = pair.second; - - for (auto& locationPtr : vrblockPtr->locations) { - - if (locationPtr->predecessors.size() > 1) { - changed = mergeRelations(locationPtr.get()) - | mergeLoads(locationPtr.get()) - | mergeRelationsByLoads(locationPtr.get()); - } else if (locationPtr->predecessors.size() == 1) { - VREdge* edge = locationPtr->predecessors[0]; - changed |= processOperation(edge->source, edge->target, edge->op.get()); - } // else no predecessors => nothing to be passed - } - } - return changed; - } - -public: - RelationsAnalyzer(const llvm::Module& m, - std::map& locs, - std::map>& blcs, - const StructureAnalyzer& sa) - : module(m), locationMapping(locs), blockMapping(blcs), structure(sa) {} - - void analyze(unsigned maxPass) { - - bool changed = true; - unsigned passNum = 0; - while (changed && ++passNum <= maxPass) - changed = analysisPass(); - } -}; - -} // namespace vr -} // namespace dg - -#endif // DG_LLVM_VALUE_RELATION_RELATIONS_ANALYZER_HPP_ diff --git a/include/dg/llvm/ValueRelations/StructureAnalyzer.h b/include/dg/llvm/ValueRelations/StructureAnalyzer.h deleted file mode 100644 index 16787e9c8..000000000 --- a/include/dg/llvm/ValueRelations/StructureAnalyzer.h +++ /dev/null @@ -1,794 +0,0 @@ -#ifndef DG_LLVM_VALUE_RELATION_STRUCTURE_ANALYZER_HPP_ -#define DG_LLVM_VALUE_RELATION_STRUCTURE_ANALYZER_HPP_ - -#include -SILENCE_LLVM_WARNINGS_PUSH -#include -#include -#include -#include -#include -SILENCE_LLVM_WARNINGS_POP - -#include - -#include "dg/AnalysisOptions.h" -#include "GraphElements.h" - -#ifndef NDEBUG -#include "getValName.h" -#endif - -namespace dg { -namespace vr { - -const llvm::Value* stripCasts(const llvm::Value* inst) { - while (auto cast = llvm::dyn_cast(inst)) - inst = cast->getOperand(0); - return inst; -} - -uint64_t getBytes(const llvm::Type* type) { - unsigned byteWidth = 8; - assert(type->isSized()); - - uint64_t size = type->getPrimitiveSizeInBits(); - assert (size % byteWidth == 0); - - return size / byteWidth; -} - -struct AllocatedSizeView{ - const llvm::Value* elementCount = nullptr; - uint64_t elementSize = 0; // in bytes - - AllocatedSizeView() = default; - AllocatedSizeView(const llvm::Value* count, uint64_t size) - : elementCount(count), elementSize(size) {} -}; - -class AllocatedArea { - const llvm::Value* ptr; - // used only if memory was allocated with realloc, as fallback when realloc fails - const llvm::Value* reallocatedPtr = nullptr; - AllocatedSizeView originalSizeView; - -public: - - AllocatedArea(const llvm::AllocaInst* alloca): ptr(alloca) { - const llvm::Type* allocatedType = alloca->getAllocatedType(); - - if (allocatedType->isArrayTy()) { - const llvm::Type* elemType = allocatedType->getArrayElementType(); - // DANGER just an arbitrary type - llvm::Type* i32 = llvm::Type::getInt32Ty(elemType->getContext()); - uint64_t intCount = allocatedType->getArrayNumElements(); - - originalSizeView = AllocatedSizeView(llvm::ConstantInt::get(i32, intCount), getBytes(elemType)); - } else { - originalSizeView = AllocatedSizeView(alloca->getOperand(0), getBytes(allocatedType)); - } - } - - AllocatedArea(const llvm::CallInst* call): ptr(call) { - const std::string& name = call->getCalledFunction()->getName().str(); - AnalysisOptions options; - - if (options.getAllocationFunction(name) == AllocationFunction::ALLOCA - || options.getAllocationFunction(name) == AllocationFunction::MALLOC) { - originalSizeView = AllocatedSizeView(call->getOperand(0), 1); - } - - if (options.getAllocationFunction(name) == AllocationFunction::CALLOC) { - auto size = llvm::cast(call->getOperand(1)); - originalSizeView = AllocatedSizeView(call->getOperand(0), size->getZExtValue()); - } - - if (options.getAllocationFunction(name) == AllocationFunction::REALLOC) { - originalSizeView = AllocatedSizeView(call->getOperand(0), 1); - reallocatedPtr = call->getOperand(0); - } - } - - const llvm::Value* getPtr() const { return ptr; } - const llvm::Value* getReallocatedPtr() const { return reallocatedPtr; } - - std::vector getAllocatedSizeViews() const { - std::vector result; - result.emplace_back(originalSizeView); - - AllocatedSizeView currentView = originalSizeView; - - while (auto op = llvm::dyn_cast(stripCasts(currentView.elementCount))) { - uint64_t size = currentView.elementSize; - - if (op->getOpcode() != llvm::Instruction::Add - && op->getOpcode() != llvm::Instruction::Mul) - // TODO could also handle subtraction of negative constant - break; - - auto c1 = llvm::dyn_cast(op->getOperand(0)); - auto c2 = llvm::dyn_cast(op->getOperand(1)); - - if (c1 && c2) { - uint64_t newCount; - uint64_t newSize; - switch (op->getOpcode()) { - - case llvm::Instruction::Add: - // XXX can these overflow? - newCount = c1->getValue().getZExtValue() - + c2->getValue().getZExtValue(); - result.emplace_back(llvm::ConstantInt::get(c1->getType(), newCount), size); - break; - - case llvm::Instruction::Mul: - // XXX can these overflow? - newSize = c1->getValue().getZExtValue() * size; - result.emplace_back(c2, newSize); - newSize = c2->getValue().getZExtValue() * size; - result.emplace_back(c1, newSize); - - newCount = c1->getValue().getZExtValue() - * c2->getValue().getZExtValue(); - result.emplace_back(llvm::ConstantInt::get(c1->getType(), newCount), size); - break; - - default: - assert (0 && "unreachable"); - } - // if we found two-constant operation, non of them can be binary - // operator to further unwrap - break; - } - - // TODO use more info here - if (!c1 && !c2) - break; - - // else one of c1, c2 is constant and the other is variable - const llvm::Value* param = nullptr; - if (c2) { - c1 = c2; param = op->getOperand(0); - } else - param = op->getOperand(1); - - // now c1 is constant and param is variable - assert (c1 && param); - - switch(op->getOpcode()) { - - case llvm::Instruction::Add: - result.emplace_back(param, size); - break; - - case llvm::Instruction::Mul: - result.emplace_back(param, size * c1->getZExtValue()); - break; - - default: - assert (0 && "unreachable"); - } - currentView = result.back(); - // reachable only in this last c1 && param case - } - return result; - } - -#ifndef NDEBUG - void ddump() const { - std::cerr << "Allocated area:" << std::endl; - std::cerr << " ptr " << debug::getValName(ptr) << std::endl; - std::cerr << " count " << debug::getValName(originalSizeView.elementCount) << std::endl; - std::cerr << " size " << originalSizeView.elementSize << std::endl; - std::cerr << std::endl; - } -#endif -}; - -struct CallRelation { - std::vector> equalPairs; - VRLocation* callSite = nullptr; -}; - -class StructureAnalyzer { - - - const llvm::Module& module; - const std::map& locationMapping; - const std::map>& blockMapping; - - // holds vector of instructions, which are processed on any path back to given location - // is computed only for locations with more than one predecessor - std::map> inloopValues; - - // holds vector of values, which are defined at given location - std::map> defined; - - const std::vector collected = { llvm::Instruction::Add, - llvm::Instruction::Sub, - llvm::Instruction::Mul }; - std::map> instructionSets; - - std::vector allocatedAreas; - - std::map> callRelationsMap; - - void categorizeEdges() { - std::set found; - - for (auto& function : module) { - if (function.isDeclaration()) - continue; - - VRBBlock* vrblockOfEntry = blockMapping.at(&function.getEntryBlock()).get(); - assert(vrblockOfEntry); - - VRLocation* first = vrblockOfEntry->first(); - - std::vector> stack; - - stack.emplace_back(first, 0); - found.emplace(first); - - VRLocation* current; - unsigned succIndex; - while (!stack.empty()) { - std::tie(current, succIndex) = stack.back(); - stack.pop_back(); - - // check if there is next successor - if (current->successors.size() <= succIndex) - continue; - - VREdge* succEdge = current->getSuccessors()[succIndex]; - VRLocation* successor = succEdge->target; - - // if there is, plan return - stack.emplace_back(current, ++succIndex); - - if (!successor) { - succEdge->type = EdgeType::TREE; - continue; - } - - // if successor was already searched, we can at least categorize the edge - if (found.find(successor) != found.end()) { - std::vector mockStack; - for (auto& locIndex : stack) - mockStack.push_back(locIndex.first); - - if (std::find(mockStack.begin(), mockStack.end(), successor) != mockStack.end()) // TODO find_if - succEdge->type = EdgeType::BACK; - else - succEdge->type = EdgeType::FORWARD; - continue; - } - - // else the successor has not been found yet, plan a visit - stack.emplace_back(successor, 0); - found.emplace(successor); - succEdge->type = EdgeType::TREE; - } - - } - } - - void findLoops() { - for (auto& pair : locationMapping) { - auto& location = pair.second; - std::vector& predEdges = location->predecessors; - if (location->isJustLoopJoin()) { - - // remove the incoming tree edge, so that backwardReach would - // really go only backwards - VREdge* treePred = nullptr; - for (auto it = predEdges.begin(); it != predEdges.end(); ++it) { - if ((*it)->type == EdgeType::TREE) { - treePred = *it; - predEdges.erase(it); - break; - } - } - assert(treePred); // every join has to have exactly one tree predecessor - - auto forwardReach = genericReach(location, true); - auto backwardReach = genericReach(location, false); - - // put the tree edge back in - predEdges.emplace_back(treePred); - - auto inloopValuesIt = inloopValues.emplace(location, - std::vector()).first; - - for (auto edge : forwardReach) { - if (std::find(backwardReach.begin(), backwardReach.end(), edge) - != backwardReach.end()) { - edge->target->inLoop = true; - if (edge->op->isInstruction()) { - auto* op = static_cast(edge->op.get()); - inloopValuesIt->second.emplace_back(op->getInstruction()); - } - } - } - } - } - } - - std::vector genericReach(VRLocation* from, bool goForward) { - std::set found = { from }; - std::list toVisit = { from }; - - std::vector result; - while (!toVisit.empty()) { - VRLocation* current = toVisit.front(); - toVisit.pop_front(); - - auto nextEdges = goForward ? current->getSuccessors() : current->getPredecessors(); - for (VREdge* nextEdge : nextEdges) { - if (!nextEdge->target) - continue; - - result.emplace_back(nextEdge); - - auto nextLocation = goForward ? nextEdge->target : nextEdge->source; - if (found.find(nextLocation) == found.end()) { - found.insert(nextLocation); - toVisit.push_back(nextLocation); - } - } - } - return result; - } - - void initializeDefined() { - - for (const llvm::Function& function : module) { - if (function.isDeclaration()) - continue; - - auto& block = function.getEntryBlock(); - VRBBlock* vrblock = blockMapping.at(&block).get(); - - std::list toVisit = { vrblock->first() }; - - // prepare sets of defined values for each location - for (auto& blockVrblockPair : blockMapping) { - for (auto& locationUPtr : *blockVrblockPair.second) - defined.emplace(locationUPtr.get(), std::set()); - } - - while (!toVisit.empty()) { - VRLocation* current = toVisit.front(); - toVisit.pop_front(); - - for (VREdge* succEdge : current->getSuccessors()) { - - // if edge leads to nowhere, just continue - VRLocation* succLoc = succEdge->target; - if (!succLoc) - continue; - - // if edge leads back, we would add values we exactly dont want - if (succEdge->type == EdgeType::BACK) - continue; - - auto& definedSucc = defined.at(succLoc); - auto& definedHere = defined.at(current); - - // copy from this location to its successor - definedSucc.insert(definedHere.begin(), definedHere.end()); - - // add instruction, if edge carries any - if (succEdge->op->isInstruction()) { - auto* op = static_cast(succEdge->op.get()); - definedSucc.emplace(op->getInstruction()); - } - - toVisit.push_back(succLoc); - } - } - } - } - - void collectInstructionSet() { - for (unsigned opcode : collected) - instructionSets.emplace(opcode, std::set()); - - for (const llvm::Function& function : module) { - for (const llvm::BasicBlock& block : function) { - for (const llvm::Instruction& inst : block) { - - // if we collect instructions with this opcode - // add it to its set - auto found = instructionSets.find(inst.getOpcode()); - if (found != instructionSets.end()) - found->second.emplace(&inst); - } - } - } - } - - bool isValidAllocationCall(const llvm::Value* val) const { - if (!llvm::isa(val)) - return false; - - const llvm::CallInst* call = llvm::cast(val); - auto function = call->getCalledFunction(); - - AnalysisOptions options; - return function && options.isAllocationFunction(function->getName().str()) - && (options.getAllocationFunction(function->getName().str()) != AllocationFunction::CALLOC - || llvm::isa(call->getOperand(1))); - - } - - void collectAllocatedAreas() { - // compute allocated areas throughout the code - for (const llvm::Function& function : module) { - for (const llvm::BasicBlock& block : function) { - for (const llvm::Instruction& inst : block) { - - if (auto alloca = llvm::dyn_cast(&inst)) { - allocatedAreas.emplace_back(alloca); - } - - else if (auto call = llvm::dyn_cast(&inst)) { - if (isValidAllocationCall(call)) { - allocatedAreas.emplace_back(call); - } - } - } - } - } - } - - void setValidAreasFromNoPredecessors(std::vector& validAreas) const { - validAreas = std::vector(allocatedAreas.size(), false); - } - - std::pair getEqualArea(const ValueRelations& graph, const llvm::Value* ptr) const { - unsigned index = 0; - const AllocatedArea* area = nullptr; - for (auto* equal : graph.getEqual(ptr)) { - std::tie(index, area) = getAllocatedAreaFor(equal); - if (area) - return { index, area }; - } - return { 0, nullptr }; - } - - void invalidateHeapAllocatedAreas(std::vector& validAreas) const { - unsigned index = 0; - for (const AllocatedArea& area : allocatedAreas) { - if (llvm::isa(area.getPtr())) - validAreas[index] = false; - ++index; - } - } - - void setValidAreasByInstruction(VRLocation* location, std::vector& validAreas, VRInstruction* vrinst) const { - const llvm::Instruction* inst = vrinst->getInstruction(); - unsigned index = 0; - const AllocatedArea* area = nullptr; - - // every memory allocated on stack is considered allocated successfully - if (llvm::isa(inst)) { - std::tie(index, area) = getAllocatedAreaFor(inst); assert(area); - validAreas[index] = true; - } - - // if came across lifetime_end call, then mark memory whose scope ended invalid - if (auto intrinsic = llvm::dyn_cast(inst)) { - if (intrinsic->getIntrinsicID() == llvm::Intrinsic::lifetime_end) { - - std::tie(index, area) = getEqualArea(location->relations, intrinsic->getOperand(1)); - assert(area); - validAreas[index] = false; - } - } - - if (auto call = llvm::dyn_cast(inst)) { - auto* function = call->getCalledFunction(); - - if (!function) - return; - - AnalysisOptions options; - if (options.getAllocationFunction(function->getName().str()) == AllocationFunction::REALLOC) { - // if realloc of memory occured, the reallocated memory cannot be considered valid - // until the realloc is proven unsuccessful - std::tie(index, area) = getEqualArea(location->relations, call->getOperand(0)); - if (area) validAreas[index] = false; - else if (!llvm::isa(call->getOperand(0))) { - // else we do not know which area has been reallocated and thus possibly invalidated, - // so it may have been any of them - invalidateHeapAllocatedAreas(validAreas); - } - } - - if (function->getName().equals("free")) { - // if free occures, the freed memory cannot be considered valid anymore - std::tie(index, area) = getEqualArea(location->relations, call->getOperand(0)); - - if (area) validAreas[index] = false; - else if (!llvm::isa(call->getOperand(0))) { - // else we do not know which area has been freed, so it may have been any of them - invalidateHeapAllocatedAreas(validAreas); - } - } - } - } - - void setValidArea(std::vector& validAreas, const AllocatedArea* area, unsigned index, bool validateThis) const { - unsigned preReallocIndex = 0; - const AllocatedArea* preReallocArea = nullptr; - if (area->getReallocatedPtr()) - std::tie(preReallocIndex, preReallocArea) = getAllocatedAreaFor(area->getReallocatedPtr()); - - if (validateThis) { - validAreas[index] = true; - if (preReallocArea) assert (!validAreas[preReallocIndex]); - - // else the original area, if any, should be validated - } else if (preReallocArea) { - validAreas[preReallocIndex] = true; - assert (!validAreas[index]); - } - } - - // if heap allocation call was just checked as successful, mark memory valid - void setValidAreasByAssumeBool(VRLocation* location, std::vector& validAreas, VRAssumeBool* assume) const { - auto icmp = llvm::dyn_cast(assume->getValue()); - if (!icmp) - return; - - auto c1 = llvm::dyn_cast(icmp->getOperand(0)); - auto c2 = llvm::dyn_cast(icmp->getOperand(1)); - - // pointer must be compared to zero // TODO? or greater - if ((c1 && c2) || (!c1 && !c2) || (!c1 && !c2->isZero()) || (!c2 && !c1->isZero())) - return; - - // get the compared parameter - const llvm::Value* param = c1 ? icmp->getOperand(1) : icmp->getOperand(0); - - unsigned index = 0; - const AllocatedArea* area = nullptr; - - for (auto equal : location->relations.getEqual(param)) { - std::tie(index, area) = getAllocatedAreaFor(equal); - // if compared pointer or equal belong to allocated area, this area - // can be marked valid - if (area) - break; - } - // the compared value is not a pointer to an allocated area - if (!area) - return; - - llvm::ICmpInst::Predicate pred = assume->getAssumption() ? - icmp->getSignedPredicate() : icmp->getInversePredicate(); - - // check that predicate implies wanted relation - switch (pred) { - case llvm::ICmpInst::Predicate::ICMP_EQ: - // if reallocated pointer is equal to zero, then original memory is still valid - setValidArea(validAreas, area, index, false); - return; - - case llvm::ICmpInst::Predicate::ICMP_NE: - // pointer is not equal to zero, therefore it a valid result of heap allocation - setValidArea(validAreas, area, index, true); - return; - - case llvm::ICmpInst::Predicate::ICMP_ULT: - case llvm::ICmpInst::Predicate::ICMP_SLT: - // if zero stands right to />=, this proves invalidity - if (c1) setValidArea(validAreas, area, index, false); - else setValidArea(validAreas, area, index, true); - return; - - case llvm::ICmpInst::Predicate::ICMP_ULE: - case llvm::ICmpInst::Predicate::ICMP_SLE: - case llvm::ICmpInst::Predicate::ICMP_UGE: - case llvm::ICmpInst::Predicate::ICMP_SGE: - // nothing to infer here, we do not get the information, whether - // pointer is zero or not - return; - - default: - assert(0 && "unreachable, would have failed in processICMP"); - } - } - - void setValidAreasFromSinglePredecessor(VRLocation* location, std::vector& validAreas) const { - // copy predecessors valid areas - VREdge* edge = location->predecessors[0]; - validAreas = edge->source->relations.getValidAreas(); - - // and alter them according to info from edge - if (edge->op->isInstruction()) - setValidAreasByInstruction(location, validAreas, static_cast(edge->op.get())); - - if (edge->op->isAssumeBool()) - setValidAreasByAssumeBool(location, validAreas, static_cast(edge->op.get())); - } - - bool trueInAll(const std::vector>& validInPreds, unsigned index) const { - for (const auto& validInPred : validInPreds) { - if (validInPred.empty() || !validInPred[index]) - return false; - } - return true; - } - - // in returned vector, false signifies that corresponding area is invalidated by some of the passed instructions - std::vector getInvalidatedAreas(const std::vector& instructions) const { - std::vector validAreas(allocatedAreas.size(), true); - - for (const llvm::Instruction* inst : instructions) { - VRLocation* location = locationMapping.at(inst); - VRInstruction vrinst(inst); - - setValidAreasByInstruction(location, validAreas, &vrinst); - } - return validAreas; - } - - void setValidAreasFromMultiplePredecessors(VRLocation* location, std::vector& validAreas) const { - std::vector> validInPreds; - - if (!location->isJustLoopJoin()) { - for (VREdge* predEdge : location->predecessors) - validInPreds.emplace_back(predEdge->source->relations.getValidAreas()); - } else { - VRLocation* treePred = nullptr; - for (VREdge* predEdge : location->predecessors) { - if (predEdge->type == EdgeType::TREE) { - treePred = predEdge->source; - break; - } - } - assert(treePred); - - validInPreds.emplace_back(treePred->relations.getValidAreas()); - validInPreds.emplace_back(getInvalidatedAreas(inloopValues.at(location))); - } - - // intersect valid areas from predecessors - for (unsigned i = 0; i < allocatedAreas.size(); ++i) { - validAreas.push_back( trueInAll(validInPreds, i) ); - } - } - - void computeValidAreas() const { - for (const llvm::Function& function : module) { - for (const llvm::BasicBlock& block : function) { - for (auto& location : blockMapping.at(&block)->locations) { - - std::vector& validAreas = location->relations.getValidAreas(); - - switch (location->predecessors.size()) { - case 0: setValidAreasFromNoPredecessors(validAreas); - break; - case 1: setValidAreasFromSinglePredecessor(location.get(), validAreas); - break; - default: setValidAreasFromMultiplePredecessors(location.get(), validAreas); - break; - } - } - } - } - } - - void initializeCallRelations() { - for (const llvm::Function& function : module) { - if (function.isDeclaration()) - continue; - - auto pair = callRelationsMap.emplace(&function, std::vector()); - - // for each location, where the function is called - for (const llvm::Value* user : function.users()) { - - // get call from user - const llvm::CallInst* call = llvm::dyn_cast(user); - if (!call) - continue; - - std::vector& callRelations = pair.first->second; - - callRelations.emplace_back(); - CallRelation& callRelation = callRelations.back(); - - // set pointer to the location from which the function is called - callRelation.callSite = locationMapping.at(call); - - // set formal parameters equal to real - unsigned argCount = 0; - for (const llvm::Argument& receivedArg : function.args()) { - if (argCount > call->getNumArgOperands()) - break; - const llvm::Value* sentArg = call->getArgOperand(argCount); - - callRelation.equalPairs.emplace_back(sentArg, &receivedArg); - ++argCount; - } - } - } - } - -public: - StructureAnalyzer( - const llvm::Module& m, - const std::map& locs, - const std::map>& blcs) - : module(m), locationMapping(locs), blockMapping(blcs) {}; - - void analyzeBeforeRelationsAnalysis() { - categorizeEdges(); - findLoops(); - collectInstructionSet(); - initializeCallRelations(); - //initializeDefined(m, blcs); - } - - void analyzeAfterRelationsAnalysis() { - collectAllocatedAreas(); - computeValidAreas(); - } - - bool isDefined(VRLocation* loc, const llvm::Value* val) const { - if (llvm::isa(val)) - return true; - - auto definedHere = defined.at(loc); - return definedHere.find(val) != definedHere.end(); - } - - // assumes that location is valid loop start (join of tree and back edges) - const std::vector& getInloopValues(VRLocation* const location) const { - return inloopValues.at(location); - } - - const std::set& getInstructionSetFor(unsigned opcode) const { - return instructionSets.at(opcode); - } - - std::pair getAllocatedAreaFor(const llvm::Value* ptr) const { - unsigned i = 0; - for (auto& area : allocatedAreas) { - if (area.getPtr() == ptr) - return { i, &area }; - ++i; - } - return { 0, nullptr }; - } - - unsigned getNumberOfAllocatedAreas() const { - return allocatedAreas.size(); - } - - const std::vector& getCallRelationsFor(const llvm::Instruction* inst) const { -#if LLVM_VERSION_MAJOR == 3 && LLVM_VERSION_MINOR <= 7 - const llvm::Function* function = inst->getParent()->getParent(); -#else - const llvm::Function* function = inst->getFunction(); -#endif - assert(function); - return callRelationsMap.at(function); - } -}; - -} // namespace vr -} // namespace dg - -#endif // DG_LLVM_VALUE_RELATION_STRUCTURE_ANALYZER_HPP_ diff --git a/include/dg/llvm/ValueRelations/ValueRelations.h b/include/dg/llvm/ValueRelations/ValueRelations.h deleted file mode 100644 index e3bdec46f..000000000 --- a/include/dg/llvm/ValueRelations/ValueRelations.h +++ /dev/null @@ -1,1803 +0,0 @@ -#ifndef DG_LLVM_RELATIONS_MAP_H_ -#define DG_LLVM_RELATIONS_MAP_H_ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -#ifndef NDEBUG - #include - #include "getValName.h" -#endif - -namespace { - -template -bool contains(const std::map& map, const Key& key) { - return map.find(key) != map.end(); -} - -template -bool contains(const std::set& set, const Val& val) { - return set.find(val) != set.end(); -} - -template -void eraseUniquePtr(std::vector>& set, const T* const value) { - auto ite = std::find_if(set.begin(), set.end(), - [&value](std::unique_ptr& ptr) -> bool { - return ptr.get() == value; - }); - assert(ite != set.end()); - set.erase(ite); -} - -template -void substitueInSet(const std::map& mapping, std::set& set) { - std::set newSet; - - for (auto& element : set) { - if (contains(mapping, element)) - newSet.insert(mapping.at(element)); - else - newSet.insert(element); - } - set.swap(newSet); -} - -template -T findByKey(const std::map& map, T key) { - auto found = map.find(key); - if (found == map.end()) - return nullptr; - return found->second; -} - -template -T findByValue(const std::map& map, T value) { - for (auto& pair : map) { - if (pair.second == value) - return pair.first; - } - return nullptr; -} - -} // namespace - -namespace dg { -namespace vr { - -class EqualityBucket; - -using BucketPtr = EqualityBucket*; -using BucketPtrSet = std::set; - -enum class Relation { EQ, NE, LE, LT, GE, GT, LOAD }; - -#ifndef NDEBUG -void dumpRelation(Relation r) { - switch(r) { - case Relation::EQ: std::cerr << "EQ"; - break; - case Relation::NE: std::cerr << "NE"; - break; - case Relation::LE: std::cerr << "LE"; - break; - case Relation::LT: std::cerr << "LT"; - break; - case Relation::GE: std::cerr << "GE"; - break; - case Relation::GT: std::cerr << "GT"; - break; - case Relation::LOAD: std::cerr << "LOAD"; - break; - } -} -#endif - -class EqualityBucket { - - using T = const llvm::Value*; - - friend class ValueRelations; - - using BucketPtr = EqualityBucket*; - using BucketPtrSet = std::set; - - BucketPtrSet lesserEqual; - BucketPtrSet lesser; - BucketPtrSet parents; - - std::vector equalities; - - struct RelatedBucket { - EqualityBucket* bucket; - Relation relation; - - RelatedBucket(): bucket(nullptr), relation(Relation::EQ) {} - - RelatedBucket(EqualityBucket* b, Relation r): bucket(b), relation(r) {} - - friend bool operator==(const RelatedBucket& lt, const RelatedBucket& rt) { - return lt.bucket == rt.bucket && lt.relation == rt.relation; - } - - friend bool operator!=(const RelatedBucket& lt, const RelatedBucket& rt) { - return !(lt == rt); - } - }; - - class BucketIterator { - - using value_type = RelatedBucket; - bool goDown = false; - bool toFirstStrict = false; - - std::vector strictlyRelated; - std::vector nonStrictlyRelated; - - EqualityBucket* start; - unsigned index = 0; - bool inStrict = true; - - RelatedBucket current; - - void computeSetsForStrict( - EqualityBucket* strictlyRelated, - std::set& strict, - const std::map& nonStrictlyRelatedMap, - const std::map& strictlyRelatedMap) { - - auto& succNonStrict = nonStrictlyRelatedMap.at(strictlyRelated); - if (!toFirstStrict) - strict.insert(succNonStrict.begin(), succNonStrict.end()); - - auto& succStrict = strictlyRelatedMap.at(strictlyRelated); - strict.insert(succStrict.begin(), succStrict.end()); - - strict.emplace(strictlyRelated); - } - - void computeSetsForNonStrict( - EqualityBucket* nonStrictlyRelated, - std::set& nonStrict, - std::set& strict, - const std::map& nonStrictlyRelatedMap) { - - // stop if the bucket is already considered strictly related - if (strict.find(nonStrictlyRelated) != strict.end()) - return; - - for (EqualityBucket* nonStrictRelated : nonStrictlyRelatedMap.at(nonStrictlyRelated)) { - // add nonStrict related bucket only if it is not already considered strictly related - if (strict.find(nonStrictRelated) == strict.end()) - nonStrict.emplace(nonStrictRelated); - } - nonStrict.emplace(nonStrictlyRelated); - } - - void computeRelated() { - std::map strictlyRelatedMap; - std::map nonStrictlyRelatedMap; - - BucketPtrSet visited; - std::stack stack; - - stack.emplace(start, goDown ? start->lesser.begin() : start->parents.begin()); - visited.emplace(start); - - while (!stack.empty()) { - DFSFrame frame = stack.top(); - stack.pop(); - - if (goDown && frame.it == frame.bucket->lesser.end()) - frame.it = frame.bucket->lesserEqual.begin(); - - if ((goDown && frame.it == frame.bucket->lesserEqual.end()) - || (!goDown && frame.it == frame.bucket->parents.end())) { - // in post order compute the set of strictly related buckets - // from the sets in its successors - auto itNonStrict = nonStrictlyRelatedMap.emplace(frame.bucket, std::set()).first; - auto itStrict = strictlyRelatedMap.emplace(frame.bucket, std::set()).first; - std::set& nonStrict = itNonStrict->second; - std::set& strict = itStrict->second; - - if (goDown) { - for (EqualityBucket* lesser : frame.bucket->lesser) - computeSetsForStrict(lesser, strict, nonStrictlyRelatedMap, strictlyRelatedMap); - - for (EqualityBucket* lesserEqual : frame.bucket->lesserEqual) { - auto& strictSucc = strictlyRelatedMap[lesserEqual]; - strict.insert(strictSucc.begin(), strictSucc.end()); - } - - for (EqualityBucket* lesserEqual : frame.bucket->lesserEqual) - computeSetsForNonStrict(lesserEqual, nonStrict, strict, nonStrictlyRelatedMap); - - } else { - for (EqualityBucket* parent : frame.bucket->parents) { - if (parent->lesser.find(frame.bucket) != parent->lesser.end()) - computeSetsForStrict(parent, strict, nonStrictlyRelatedMap, strictlyRelatedMap); - else { // else this bucket is lesserEqual to parent - auto& strictSucc = strictlyRelatedMap[parent]; - strict.insert(strictSucc.begin(), strictSucc.end()); - } - } - - for (EqualityBucket* parent : frame.bucket->parents) { - if (parent->lesserEqual.find(frame.bucket) != parent->lesserEqual.end()) - computeSetsForNonStrict(parent, nonStrict, strict, nonStrictlyRelatedMap); - } - } - - continue; - } - - // plan visit for the next successor - stack.emplace(frame.bucket, std::next(frame.it)); - - // plan visit to the current successor - if (visited.find(*frame.it) == visited.end()) { - visited.emplace(*frame.it); - if (goDown) - stack.emplace(*frame.it, (*frame.it)->lesser.begin()); - else - stack.emplace(*frame.it, (*frame.it)->parents.begin()); - } - } - const auto& strictSet = strictlyRelatedMap[start]; - strictlyRelated.insert(strictlyRelated.end(), strictSet.begin(), strictSet.end()); - - const auto& nonStrictSet = nonStrictlyRelatedMap[start]; - nonStrictlyRelated.insert(nonStrictlyRelated.end(), nonStrictSet.begin(), nonStrictSet.end()); - } - -public: - - struct DFSFrame { - EqualityBucket* bucket; - typename BucketPtrSet::iterator it; - - DFSFrame(EqualityBucket* b, typename BucketPtrSet::iterator i) - : bucket(b), it(i) {} - }; - - BucketIterator() = default; - BucketIterator(EqualityBucket* s, bool down, bool strict, bool begin) - : goDown(down), toFirstStrict(strict), start(s), current(start, Relation::EQ) { - if (!begin) { - current = RelatedBucket(nullptr, Relation::EQ); - return; - } - - computeRelated(); - } - - friend bool operator==(const BucketIterator& lt, const BucketIterator& rt) { - return lt.goDown == rt.goDown - && lt.toFirstStrict == rt.toFirstStrict - && lt.current == rt.current; - } - - friend bool operator!=(const BucketIterator& lt, const BucketIterator& rt) { - return !(lt == rt); - } - - const value_type& operator*() const { return current; } - const value_type* operator->() const { return ¤t; } - - value_type operator*() { return current; } - value_type* operator->() { return ¤t; } - - BucketIterator& operator++() { - - if (inStrict && index >= strictlyRelated.size()) { - index = 0; - inStrict = false; - } - - if (!inStrict && index >= nonStrictlyRelated.size()) { - current = RelatedBucket(nullptr, Relation::EQ); - return *this; - } - - if (inStrict) - current = RelatedBucket(strictlyRelated[index], goDown ? Relation::LT : Relation::GT); - else - current = RelatedBucket(nonStrictlyRelated[index], goDown ? Relation::LE : Relation::GE); - - ++index; - return *this; - } - - BucketIterator operator++(int) { - auto preInc = *this; - ++(*this); - return preInc; - } - - }; - - BucketIterator begin_down() { - return BucketIterator(this, true, false, true); - } - - BucketIterator end_down() { - return BucketIterator(this, true, false, false); - } - - BucketIterator begin_up() { - return BucketIterator(this, false, false, true); - } - - BucketIterator end_up() { - return BucketIterator(this, false, false, false); - } - - // iterates over buckets up to the first strictly lesser on each path - BucketIterator begin_strictDown() { - return BucketIterator(this, true, true, true); - } - - BucketIterator end_strictDown() { - return BucketIterator(this, true, true, false); - } - - // iterates over buckets up to the first strictly greater on each path - BucketIterator begin_strictUp() { - return BucketIterator(this, false, true, true); - } - - BucketIterator end_strictUp() { - return BucketIterator(this, false, true, false); - } - - bool subtreeContains( - EqualityBucket* needle, - bool ignoreLE) { - - for (auto it = begin_down(); it != end_down(); ++it) { - - if (it->bucket == needle) { - if (ignoreLE && it->relation != Relation::LT) - break; - // else we found searched bucket - return true; - } - } - return false; - } - - // return path from bucket to this - std::vector getLesserEqualPath(EqualityBucket* bucket) { - std::vector result; - - BucketPtrSet visited; - std::stack stack; - - stack.emplace(this, lesserEqual.begin()); - visited.emplace(this); - - while (!stack.empty()) { - BucketIterator::DFSFrame frame = stack.top(); - stack.pop(); - - // if we found the searched bucket, reconstruct the path and return - if (frame.bucket == bucket) { - std::vector result; - result.emplace_back(bucket); - - while (!stack.empty()) { - frame = stack.top(); - stack.pop(); - result.emplace_back(frame.bucket); - } - return result; - } - - if (frame.it == frame.bucket->lesserEqual.end()) - continue; - - // plan visit for the next successor - stack.emplace(frame.bucket, std::next(frame.it)); - - // plan visit to the current successor - if (visited.find(*frame.it) == visited.end()) { - visited.emplace(*frame.it); - stack.emplace(*frame.it, (*frame.it)->lesserEqual.begin()); - } - } - - assert(0 && "unreachable"); - abort(); - } - - void mergeConnections(const EqualityBucket& other) { - // set_union does't work in place - lesserEqual.insert(other.lesserEqual.begin(), other.lesserEqual.end()); - for (EqualityBucket* bucketPtr : other.lesserEqual) - bucketPtr->parents.insert(this); - - lesser.insert(other.lesser.begin(), other.lesser.end()); - for (EqualityBucket* bucketPtr : other.lesser) - bucketPtr->parents.insert(this); - - parents.insert(other.parents.begin(), other.parents.end()); - for (EqualityBucket* parent : other.parents) { - if (contains(parent->lesserEqual, const_cast(&other))) - parent->lesserEqual.insert(this); - else if (contains(parent->lesser, const_cast(&other))) - parent->lesser.insert(this); - else - assert(0); // was a parent so it must have been lesser or lesserEqual - } - - equalities.insert(equalities.end(), - other.equalities.begin(), other.equalities.end()); - } - - void disconnectAll() { - for (auto* parent : parents) { - parent->lesserEqual.erase(this); - parent->lesser.erase(this); - } - parents.clear(); - - for (auto* bucketPtr : lesserEqual) { - bucketPtr->parents.erase(this); - } - lesserEqual.clear(); - - for (auto* bucketPtr : lesser) { - bucketPtr->parents.erase(this); - } - lesser.clear(); - } - - void substitueAll(const std::map& oldToNewPtr) { - substitueInSet(oldToNewPtr, lesserEqual); - substitueInSet(oldToNewPtr, lesser); - substitueInSet(oldToNewPtr, parents); - } - - std::vector getDirectlyRelated(bool goDown) { - std::vector result; - - for (auto it = (goDown ? begin_strictDown() : begin_strictUp()); - it != (goDown ? end_strictDown() : end_strictUp()); - ++it) { - - if ((goDown && it->relation == Relation::LT) || (!goDown && it->relation == Relation::GT)) - result.emplace_back(it->bucket); - } - - return result; - } - - std::vector& getEqual() { - return equalities; - } - - const std::vector& getEqual() const { - return equalities; - } - - T getAny() const { - assert(equalities.size() > 0); - return equalities[0]; - } - - bool hasAllEqualitiesFrom(const EqualityBucket* other) const { - for (T val : other->equalities) { - if (std::find(equalities.begin(), equalities.end(), val) == equalities.end()) - return false; - } - return true; - } -}; - -class ValueRelations { - - using T = const llvm::Value*; - using C = const llvm::ConstantInt*; - -private: - std::vector> buckets; - std::map mapToBucket; - std::map placeholderBuckets; - unsigned lastPlaceholderId = 0; - - std::map> nonEqualities; - - // map of pairs (a, b) such that {any of b} = load {any of a} - std::map loads; - - std::vector validAreas; - - struct ValueIterator { - using value_type = std::pair; - - enum Type { UP, DOWN, ALL, NONE }; - - Type type = Type::NONE; - bool strictOnly = false; - EqualityBucket* start; - EqualityBucket::BucketIterator it; - unsigned index; - - ValueIterator(EqualityBucket* st, bool s, Type t, bool begin) - : type(t), strictOnly(s), start(st), index(0) { - if (begin) { - if (type == Type::DOWN || type == Type::ALL) - it = start->begin_down(); - if (type == Type::UP) - it = start->begin_up(); - toNextValidValue(); - } else { - if (type == Type::DOWN) - it = start->end_down(); - if (type == Type::UP || type == Type::ALL) - it = start->end_up(); - } - } - - friend bool operator==(const ValueIterator& lt, const ValueIterator& rt) { - return lt.type == rt.type - && lt.strictOnly == rt.strictOnly - && lt.it == rt.it; - } - - friend bool operator!=(const ValueIterator& lt, const ValueIterator& rt) { - return !(lt == rt); - } - - value_type operator*() const { - if (strictOnly && it->relation != Relation::LT && it->relation != Relation::GT) - assert(0 && "iterator always stops only at strict if demanded"); - return { it->bucket->getEqual()[index], it->relation }; - } - - // make iterator always point at valid value or end - ValueIterator& operator++() { - if (it == start->end_up() || it == start->end_down()) - return *this; - // we dont have to check if type == ALL because code later - // handles the jump between iterators - - if (index + 1 < it->bucket->equalities.size()) { - ++index; - return *this; - } - - // else we need to move on to the next bucket - ++it; - index = 0; - toNextValidValue(); - - if (it == start->end_down() && type == Type::ALL) { - it = ++(start->begin_up()); // ++ so that we would not pass equal again - toNextValidValue(); - } - - return *this; - } - - ValueIterator operator++(int) { - auto preInc = *this; - ++(*this); - return preInc; - } - - private: - void toNextValidValue() { - while (it != start->end_down() - && it != start->end_up() - && (it->bucket->getEqual().empty() - || (strictOnly && it->relation != Relation::LT && it->relation != Relation::GT))) - ++it; - } - }; - - bool inGraph(T val) const { - return contains(mapToBucket, val); - } - - bool inGraph(EqualityBucket* bucket) const { - return bucket; - } - - bool hasComparativeRelations(EqualityBucket* bucket) const { - return bucket->getEqual().size() > 1 - || nonEqualities.find(bucket) != nonEqualities.end() - || ++bucket->begin_down() != bucket->end_down() - || ++bucket->begin_up() != bucket->end_up(); - } - - bool hasComparativeRelationsOrLoads(EqualityBucket* bucket) const { - return hasComparativeRelations(bucket) - || findByKey(loads, bucket) - || findByValue(loads, bucket); - } - - EqualityBucket* getCorrespondingBucket(const ValueRelations& other, EqualityBucket* otherBucket) const { - if (!otherBucket->getEqual().empty()) { - auto found = mapToBucket.find(otherBucket->getEqual()[0]); - if (found != mapToBucket.end()) - return found->second; - return nullptr; - } - - // else this is placeholder bucket - EqualityBucket* otherFromBucket = findByValue(other.loads, otherBucket); - assert(otherFromBucket); - assert(!otherFromBucket->getEqual().empty()); - // if bucket is empty, it surely has a nonempty load bucket, - // they aren't created under different circumstances - - T from = otherFromBucket->getEqual()[0]; - if (hasLoad(from)) - return loads.at(mapToBucket.at(from)); - return nullptr; - } - - EqualityBucket* getCorrespondingBucketOrNew(const ValueRelations& other, EqualityBucket* otherBucket) { - if (!otherBucket->getEqual().empty()) { - const auto& equalities = otherBucket->getEqual(); - - for (T val : equalities) { - auto found = mapToBucket.find(val); - if (found != mapToBucket.end()) - return found->second; - } - add(equalities[0]); - return mapToBucket.find(equalities[0])->second; - } - - // else this is placeholder bucket - EqualityBucket* otherFromBucket = findByValue(other.loads, otherBucket); - assert(otherFromBucket); - assert(!otherFromBucket->getEqual().empty()); - // if bucket is empty, it surely has a nonempty load bucket, - // they aren't created under different circumstances - - for (T from : otherFromBucket->getEqual()) { - if (hasLoad(from)) - return loads[mapToBucket[from]]; - } - unsigned placeholder = newPlaceholderBucket(); - setLoad(otherFromBucket->getEqual()[0], placeholder); - return placeholderBuckets[placeholder]; - } - - std::vector> - getExtraRelationsIn(const ValueRelations& other) const { - std::vector> result; - - for (auto& bucketUniquePtr : other.buckets) { - - EqualityBucket* otherBucket = bucketUniquePtr.get(); - EqualityBucket* thisBucket = getCorrespondingBucket(other, otherBucket); - - if (!thisBucket || !thisBucket->hasAllEqualitiesFrom(otherBucket)) - result.emplace_back(otherBucket, otherBucket, Relation::EQ); - - // find unrelated comparative buckets - for (auto it = otherBucket->begin_down(); it != otherBucket->end_down(); ++it) { - - if (it->relation == Relation::EQ) - continue; // already handled prior to loop - - EqualityBucket* otherRelatedBucket = it->bucket; - EqualityBucket* thisRelatedBucket = getCorrespondingBucket(other, otherRelatedBucket); - - if (!thisBucket - || !thisRelatedBucket - || (it->relation == Relation::LT && !isLesser(thisRelatedBucket, thisBucket)) - || (it->relation == Relation::LE && !isLesserEqual(thisRelatedBucket, thisBucket))) - result.emplace_back(otherRelatedBucket, otherBucket, it->relation); - } - - // find urelated non-equal buckets - auto foundNE = other.nonEqualities.find(otherBucket); - if (foundNE != other.nonEqualities.end()) { - for (EqualityBucket* otherRelatedBucket : foundNE->second) { - EqualityBucket* thisRelatedBucket = getCorrespondingBucket(other, otherRelatedBucket); - - if (!thisBucket - || !thisRelatedBucket - || !isNonEqual(thisRelatedBucket, thisBucket)) - result.emplace_back(otherRelatedBucket, otherBucket, Relation::NE); - } - } - - // found unrelated load buckets - auto foundLoad = other.loads.find(otherBucket); - if (foundLoad != other.loads.end()) { - EqualityBucket* otherRelatedBucket = foundLoad->second; - EqualityBucket* thisRelatedBucket = getCorrespondingBucket(other, otherRelatedBucket); - - if (!thisBucket - || !thisRelatedBucket - || !isLoad(thisBucket, thisRelatedBucket)) - result.emplace_back(otherRelatedBucket, otherBucket, Relation::LOAD); - } - } - - return result; - } - - std::vector getBucketsToMerge(BucketPtr newBucketPtr, BucketPtr oldBucketPtr) const { - - if (!isLesserEqual(newBucketPtr, oldBucketPtr) && !isLesserEqual(oldBucketPtr, newBucketPtr)) - return { newBucketPtr, oldBucketPtr }; - - // else handle lesserEqual specializing to equal - std::vector toMerge; - if (isLesserEqual(newBucketPtr, oldBucketPtr)) { - toMerge = oldBucketPtr->getLesserEqualPath(newBucketPtr); - } else { - toMerge = newBucketPtr->getLesserEqualPath(oldBucketPtr); - } - - // unset unnecessary lesserEqual relations - for (auto it = ++toMerge.begin(); it != toMerge.end(); ++it) { - EqualityBucket* below = *std::prev(it); - EqualityBucket* above = *it; - - above->lesserEqual.erase(below); - below->parents.erase(above); - } - - return toMerge; - } - - void setEqual(EqualityBucket* newBucketPtr, EqualityBucket* oldBucketPtr) { - - if (isEqual(newBucketPtr, oldBucketPtr)) - return; - - assert(!hasConflictingRelation(newBucketPtr, oldBucketPtr, Relation::EQ)); - - std::vector toMerge = getBucketsToMerge(newBucketPtr, oldBucketPtr); - - newBucketPtr = toMerge[0]; - - for (auto it = ++toMerge.begin(); it != toMerge.end(); ++it) { - - oldBucketPtr = *it; - - // replace nonEquality info to regard only remaining bucket - auto newNEIt = nonEqualities.find(newBucketPtr); - auto oldNEIt = nonEqualities.find(oldBucketPtr); - - if (oldNEIt != nonEqualities.end()) { - for (EqualityBucket* nonEqual : oldNEIt->second) { - nonEqualities.at(nonEqual).emplace(newBucketPtr); - nonEqualities.at(nonEqual).erase(oldBucketPtr); - } - - oldNEIt->second.erase(newBucketPtr); - if (newNEIt != nonEqualities.end()) - newNEIt->second.insert(oldNEIt->second.begin(), oldNEIt->second.end()); - else - nonEqualities.emplace(newBucketPtr, oldNEIt->second); - - nonEqualities.erase(oldBucketPtr); - } - - // replace mapToBucket info to regard only remaining bucket - for (auto& pair : mapToBucket) { - if (pair.second == oldBucketPtr) - pair.second = newBucketPtr; - } - - // replace load info to regard only remaining bucket - for (auto pairIt = loads.begin(); pairIt != loads.end(); ++pairIt) { - if (pairIt->first == oldBucketPtr) { - loads.emplace(newBucketPtr, - pairIt->second == oldBucketPtr ? newBucketPtr : pairIt->second); // in case x = load x - pairIt = loads.erase(pairIt); - } - - if (pairIt->second == oldBucketPtr) - pairIt->second = newBucketPtr; - } - - // make successors and parents of right belong to left too - newBucketPtr->mergeConnections(*oldBucketPtr); - - // make successors and parents of right forget it - oldBucketPtr->disconnectAll(); - - // replace placeholder info to disregard removed bucket - for (auto pair : placeholderBuckets) { - if (pair.second == oldBucketPtr) { - placeholderBuckets.erase(pair.first); - break; - } - } - - // remove right - eraseUniquePtr(buckets, oldBucketPtr); - } - } - - void setNonEqual(EqualityBucket* ltBucketPtr, EqualityBucket* rtBucketPtr) { - - if (isNonEqual(ltBucketPtr, rtBucketPtr)) - return; - - assert(!hasConflictingRelation(ltBucketPtr, rtBucketPtr, Relation::NE)); - - // TODO? handle lesserEqual specializing to lesser - - auto foundLt = nonEqualities.find(ltBucketPtr); - if (foundLt != nonEqualities.end()) - foundLt->second.emplace(rtBucketPtr); - else - nonEqualities.emplace(ltBucketPtr, std::set{rtBucketPtr}); - - auto foundRt = nonEqualities.find(rtBucketPtr); - if (foundRt != nonEqualities.end()) - foundRt->second.emplace(ltBucketPtr); - else - nonEqualities.emplace(rtBucketPtr, std::set{ltBucketPtr}); - } - - void setLesser(EqualityBucket* ltBucketPtr, EqualityBucket* rtBucketPtr) { - if (isLesser(ltBucketPtr, rtBucketPtr)) - return; - - assert(!hasConflictingRelation(ltBucketPtr, rtBucketPtr, Relation::LT)); - - if (isLesserEqual(ltBucketPtr, rtBucketPtr)) { - if (contains(rtBucketPtr->lesserEqual, ltBucketPtr)) - rtBucketPtr->lesserEqual.erase(ltBucketPtr); - //else - // assert(0); // more buckets in between, can't decide this - } - - rtBucketPtr->lesser.insert(ltBucketPtr); - ltBucketPtr->parents.insert(rtBucketPtr); - } - - void setLesserEqual(EqualityBucket* ltBucketPtr, EqualityBucket* rtBucketPtr) { - if (isLesserEqual(ltBucketPtr, rtBucketPtr)) - return; - if (isNonEqual(ltBucketPtr, rtBucketPtr)) - return setLesser(ltBucketPtr, rtBucketPtr); - - assert(!hasConflictingRelation(ltBucketPtr, rtBucketPtr, Relation::LE)); - - // infer values being equal - if (isLesserEqual(rtBucketPtr, ltBucketPtr)) - return setEqual(ltBucketPtr, rtBucketPtr); - - rtBucketPtr->lesserEqual.insert(ltBucketPtr); - ltBucketPtr->parents.insert(rtBucketPtr); - } - - void setLoad(EqualityBucket* fromBucketPtr, EqualityBucket* valBucketPtr) { - if (isLoad(fromBucketPtr, valBucketPtr)) - return; - - // get set of values that load from equal pointers - EqualityBucket* valEqualBucketPtr = findByKey(loads, fromBucketPtr); - - // if there is such a set, we just add val to it - if (valEqualBucketPtr) { - setEqual(valBucketPtr, valEqualBucketPtr); - } else { - loads.emplace(fromBucketPtr, valBucketPtr); - } - } - - bool isEqual(EqualityBucket* ltEqBucket, EqualityBucket* rtEqBucket) const { - - return ltEqBucket == rtEqBucket; - } - - bool isNonEqual(EqualityBucket* ltEqBucket, EqualityBucket* rtEqBucket) const { - auto found = nonEqualities.find(ltEqBucket); - if (found == nonEqualities.end()) - return false; - - return found->second.find(rtEqBucket) != found->second.end(); - } - - C getEqualConstant(EqualityBucket* ltEqBucket) const { - C ltConst = nullptr; - for (const llvm::Value* val : ltEqBucket->getEqual()) { - if (auto constant = llvm::dyn_cast(val)) - ltConst = constant; - } - - return ltConst; - } - - bool isLesser(EqualityBucket* ltEqBucket, EqualityBucket* rtEqBucket) const { - return rtEqBucket->subtreeContains(ltEqBucket, true); - } - - bool isLesserEqual(EqualityBucket* ltEqBucket, EqualityBucket* rtEqBucket) const { - return rtEqBucket->subtreeContains(ltEqBucket, false); - } - - // in case of LOAD, rt is the from and lt is val - bool hasConflictingRelation( - EqualityBucket* ltBucketPtr, - EqualityBucket* rtBucketPtr, - Relation relation) const { - switch (relation) { - case Relation::EQ: - return isNonEqual(ltBucketPtr, rtBucketPtr) - || isLesser(ltBucketPtr, rtBucketPtr) - || isLesser(rtBucketPtr, ltBucketPtr); - - case Relation::NE: - return isEqual(ltBucketPtr, rtBucketPtr); - - case Relation::LT: - return isLesserEqual(rtBucketPtr, ltBucketPtr); - - case Relation::LE: - return isLesser(rtBucketPtr, ltBucketPtr); - - case Relation::GT: - return hasConflictingRelation(rtBucketPtr, ltBucketPtr, Relation::LT); - - case Relation::GE: - return hasConflictingRelation(rtBucketPtr, ltBucketPtr, Relation::LE); - - case Relation::LOAD: - return hasLoad(rtBucketPtr) - && hasConflictingRelation(ltBucketPtr, loads.at(rtBucketPtr), Relation::EQ); - } - assert(0 && "unreachable"); - abort(); - } - - bool isLoad(EqualityBucket* fromBucketPtr, EqualityBucket* valBucketPtr) const { - auto found = loads.find(fromBucketPtr); - return found != loads.end() && valBucketPtr == found->second; - } - - bool hasLoad(EqualityBucket* fromBucketPtr) const { - return loads.find(fromBucketPtr) != loads.end(); - } - - void eraseBucketIfUnrelated(EqualityBucket* bucket) { - if (hasComparativeRelationsOrLoads(bucket)) - return; - - for (auto& pair : mapToBucket) { - if (pair.second == bucket) { - mapToBucket.erase(pair.first); - break; - } - } - - eraseUniquePtr(buckets, bucket); - } - - void unsetComparativeRelations(EqualityBucket* valBucketPtr) { - // save related buckets to check later - BucketPtrSet allRelated; - allRelated.insert(valBucketPtr->parents.begin(), valBucketPtr->parents.end()); - allRelated.insert(valBucketPtr->lesser.begin(), valBucketPtr->lesser.end()); - allRelated.insert(valBucketPtr->lesserEqual.begin(), valBucketPtr->lesserEqual.end()); - - auto found = nonEqualities.find(valBucketPtr); - if (found != nonEqualities.end()) - allRelated.insert(found->second.begin(), found->second.end()); - - // overconnect parents to children - for (EqualityBucket* parent : valBucketPtr->parents) { - - for (EqualityBucket* lesser : valBucketPtr->lesser) { - parent->lesser.emplace(lesser); - lesser->parents.emplace(parent); - } - - for (EqualityBucket* lesserEqual : valBucketPtr->lesserEqual) { - - if (parent->lesserEqual.find(valBucketPtr) != parent->lesserEqual.end()) - parent->lesserEqual.emplace(lesserEqual); - else - parent->lesser.emplace(lesserEqual); - lesserEqual->parents.emplace(parent); - } - } - - nonEqualities.erase(valBucketPtr); - for (auto& pair : nonEqualities) { - pair.second.erase(valBucketPtr); - } - - // it severes all ties with the rest of the graph - valBucketPtr->disconnectAll(); - - // remove buckets that lost their only relation - for (EqualityBucket* bucket : allRelated) - eraseBucketIfUnrelated(bucket); - } - - C getLowerBound(EqualityBucket* bucket, bool strict) const { - - C highest = nullptr; - for (auto it = bucket->begin_down(); it != bucket->end_down(); ++it) { - C constant = getEqualConstant(it->bucket); - - if ((!strict || it->relation == Relation::LT) // ignore strict values if demanded - && (!highest || (constant && constant->getSExtValue() > highest->getSExtValue()))) - highest = constant; - } - return highest; - } - - C getUpperBound(EqualityBucket* bucket, bool strict) const { - - C lowest = nullptr; - for (auto it = bucket->begin_up(); it != bucket->end_up(); ++it) { - C constant = getEqualConstant(it->bucket); - - if ((!strict || it->relation == Relation::GT) - && (!lowest || (constant && constant->getSExtValue() < lowest->getSExtValue()))) - lowest = constant; - } - return lowest; - } - - std::vector getDirectlyRelated(T val, bool goDown) const { - if (!inGraph(val)) - return {}; - EqualityBucket* bucketPtr = mapToBucket.at(val); - - std::vector relatedBuckets = bucketPtr->getDirectlyRelated(goDown); - - std::vector result; - for (EqualityBucket* bucketPtr : relatedBuckets) { - if (!bucketPtr->getEqual().empty()) - result.emplace_back(bucketPtr->getAny()); - } - return result; - } - - EqualityBucket* getBucket(T val) const { - auto it = mapToBucket.find(val); - return it != mapToBucket.end() ? it->second : nullptr; - } - -public: - - ValueRelations() = default; - - ValueRelations(const ValueRelations& other): - lastPlaceholderId(other.lastPlaceholderId) { - - std::map oldToNewPtr; - - // create new copies of buckets - for(const std::unique_ptr& bucketUniquePtr : other.buckets) { - assert(bucketUniquePtr); - assert(bucketUniquePtr.get()); - - EqualityBucket* newBucketPtr = new EqualityBucket(*bucketUniquePtr); - buckets.emplace_back(newBucketPtr); - - oldToNewPtr.emplace(bucketUniquePtr.get(), newBucketPtr); - } - - // set successors to point to new copies - for (const std::unique_ptr& bucketUniquePtr : buckets) - bucketUniquePtr->substitueAll(oldToNewPtr); - - // set map to use new copies - for (auto& pair : other.mapToBucket) - mapToBucket.emplace(pair.first, oldToNewPtr[pair.second]); - - // set placeholder buckets to use new copies - for (auto& pair : other.placeholderBuckets) - placeholderBuckets.emplace(pair.first, oldToNewPtr[pair.second]); - - // set nonEqualities to use new copies - for (auto& pair : other.nonEqualities) { - auto returnPair = nonEqualities.emplace(oldToNewPtr[pair.first], pair.second); - substitueInSet(oldToNewPtr, returnPair.first->second); - } - - // set loads to use new copies - for (auto& pair : other.loads) - loads.emplace(oldToNewPtr[pair.first], oldToNewPtr[pair.second]); - - } - - friend void swap(ValueRelations& first, ValueRelations& second) { - using std::swap; - - swap(first.buckets, second.buckets); - swap(first.mapToBucket, second.mapToBucket); - swap(first.placeholderBuckets, second.placeholderBuckets); - swap(first.lastPlaceholderId, second.lastPlaceholderId); - swap(first.nonEqualities, second.nonEqualities); - swap(first.loads, second.loads); - } - - ValueRelations& operator=(ValueRelations other) { - swap(*this, other); - - return *this; - } - - bool hasAllRelationsFrom(const ValueRelations& other) const { - return getExtraRelationsIn(other).empty(); - } - - bool merge(const ValueRelations& other, bool relationsOnly = false) { - ValueRelations original = *this; - - std::vector> missingRelations; - missingRelations = getExtraRelationsIn(other); - - EqualityBucket* otherBucket; - EqualityBucket* otherRelatedBucket; - Relation relation; - for (auto& tuple : missingRelations) { - std::tie(otherRelatedBucket, otherBucket, relation) = tuple; - - EqualityBucket* thisBucket = getCorrespondingBucketOrNew(other, otherBucket); - EqualityBucket* thisRelatedBucket = getCorrespondingBucketOrNew(other, otherRelatedBucket); - assert(thisBucket && thisRelatedBucket); - - if (hasConflictingRelation(thisRelatedBucket, thisBucket, relation)) { - swap(*this, original); - return false; - } - - switch (relation) { - case Relation::EQ: - for (T val : otherRelatedBucket->getEqual()) { - if (hasConflictingRelation(val, otherRelatedBucket->getEqual()[0], Relation::EQ)) { - swap(*this, original); - return false; - } - add(val); - setEqual(thisRelatedBucket, mapToBucket[val]); - thisRelatedBucket = getCorrespondingBucketOrNew(other, otherRelatedBucket); - } - break; - case Relation::NE: setNonEqual(thisRelatedBucket, thisBucket); - break; - case Relation::LT: setLesser(thisRelatedBucket, thisBucket); - break; - case Relation::LE: setLesserEqual(thisRelatedBucket, thisBucket); - break; - case Relation::LOAD: if (!relationsOnly) - setLoad(thisBucket, thisRelatedBucket); - break; - default: assert(0 && "GE and GT cannot occurr"); - } - } - - return true; - } - - EqualityBucket* add(T val) { - if (EqualityBucket* bucket = getBucket(val)) - return bucket; - - auto constVal = llvm::dyn_cast(val); - if (constVal) { - for (auto& bucketUniquePtr : buckets) { - EqualityBucket* otherBucket = bucketUniquePtr.get(); - C constBucket = getEqualConstant(otherBucket); - if (!constBucket) - continue; - - int64_t newInt = constVal->getSExtValue(); - int64_t oldInt = constBucket->getSExtValue(); - - if (newInt == oldInt) { - mapToBucket.emplace(val, otherBucket); - otherBucket->getEqual().emplace_back(val); - return otherBucket; - } - } - } - - // else added value will surely be in a bucket of its own - EqualityBucket* newBucketPtr = new EqualityBucket; - buckets.emplace_back(newBucketPtr); - mapToBucket.emplace(val, newBucketPtr); - newBucketPtr->getEqual().emplace_back(val); - - if (!constVal) - return newBucketPtr; - // else add all relations to other constants - - for (auto& bucketUniquePtr : buckets) { - EqualityBucket* otherBucket = bucketUniquePtr.get(); - C constBucket = getEqualConstant(otherBucket); - if (!constBucket) - continue; - - int64_t newInt = constVal->getSExtValue(); - int64_t oldInt = constBucket->getSExtValue(); - - if (newInt < oldInt) setLesser(newBucketPtr, otherBucket); - if (newInt > oldInt) setLesser(otherBucket, newBucketPtr); - } - - return newBucketPtr; - } - - // DANGER setEqual invalidates all EqualityBucket* - void setEqual(T lt, T rt) { - EqualityBucket* ltBucket = add(lt); - EqualityBucket* rtBucket = add(rt); - setEqual(ltBucket, rtBucket); - } - - void setEqual(T lt, unsigned rt) { - EqualityBucket* ltBucket = add(lt); - setEqual(ltBucket, placeholderBuckets[rt]); - } - - void setEqual(unsigned lt, T rt) { - setEqual(rt, lt); - } - - void setNonEqual(T lt, T rt) { - EqualityBucket* ltBucket = add(lt); - EqualityBucket* rtBucket = add(rt); - setNonEqual(ltBucket, rtBucket); - } - - void setNonEqual(T lt, unsigned rt) { - EqualityBucket* ltBucket = add(lt); - setNonEqual(ltBucket, placeholderBuckets[rt]); - } - - void setNonEqual(unsigned lt, T rt) { - EqualityBucket* rtBucket = add(rt); - setNonEqual(placeholderBuckets[lt], rtBucket); - } - - void setLesser(T lt, T rt) { - EqualityBucket* ltBucket = add(lt); - EqualityBucket* rtBucket = add(rt); - setLesser(ltBucket, rtBucket); - } - - void setLesser(T lt, unsigned rt) { - EqualityBucket* ltBucket = add(lt); - setLesser(ltBucket, placeholderBuckets[rt]); - } - - void setLesser(unsigned lt, T rt) { - EqualityBucket* rtBucket = add(rt); - setLesser(placeholderBuckets[lt], rtBucket); - } - - void setLesserEqual(T lt, T rt) { - EqualityBucket* ltBucket = add(lt); - EqualityBucket* rtBucket = add(rt); - setLesserEqual(ltBucket, rtBucket); - } - - void setLesserEqual(T lt, unsigned rt) { - EqualityBucket* ltBucket = add(lt); - setLesserEqual(ltBucket, placeholderBuckets[rt]); - } - - void setLesserEqual(unsigned lt, T rt) { - EqualityBucket* rtBucket = add(rt); - setLesserEqual(placeholderBuckets[lt], rtBucket); - } - - void setLoad(T from, T val) { - EqualityBucket* valBucket = add(val); - EqualityBucket* fromBucket = add(from); - setLoad(fromBucket, valBucket); - } - - void setLoad(T from, unsigned val) { - EqualityBucket* fromBucket = add(from); - setLoad(fromBucket, placeholderBuckets[val]); - } - - void unsetAllLoadsByPtr(T from) { - EqualityBucket* fromBucketPtr = getBucket(from); - if (!inGraph(fromBucketPtr)) - return; - - EqualityBucket* valBucketPtr = findByKey(loads, fromBucketPtr); - if (!inGraph(valBucketPtr)) - return; // from doesn't load anything - - loads.erase(fromBucketPtr); - - for (auto& pair : placeholderBuckets) { - if (pair.second == valBucketPtr) { - unsetComparativeRelations(valBucketPtr); - placeholderBuckets.erase(pair.first); - break; - } - } - - if (!hasComparativeRelationsOrLoads(valBucketPtr)) { - if (!valBucketPtr->getEqual().empty()) { - T val = valBucketPtr->getAny(); - mapToBucket.erase(val); - } - eraseUniquePtr(buckets, valBucketPtr); - } - if (!hasComparativeRelationsOrLoads(fromBucketPtr)) { - mapToBucket.erase(from); - eraseUniquePtr(buckets, fromBucketPtr); - } - } - - void unsetAllLoads() { - loads.clear(); - - for (auto it = buckets.begin(); it != buckets.end(); ) { - if (!hasComparativeRelations(it->get())) { - if (!(*it)->getEqual().empty()) - mapToBucket.erase((*it)->getAny()); - - it = buckets.erase(it); - } else - ++it; - } - } - - void unsetComparativeRelations(T val) { - EqualityBucket* valBucketPtr = getBucket(val); - if (!inGraph(valBucketPtr)) - return; - - bool onlyReference = valBucketPtr->getEqual().size() == 1; - if (!onlyReference) { - // val moves to its own equality bucket - mapToBucket.erase(val); - add(val); - } else - unsetComparativeRelations(valBucketPtr); - } - - bool isEqual(T lt, T rt) const { - - C constLt = llvm::dyn_cast(lt); - C constRt = llvm::dyn_cast(rt); - - EqualityBucket* ltBucketPtr = getBucket(lt); - EqualityBucket* rtBucketPtr = getBucket(rt); - - if (!inGraph(ltBucketPtr) && !inGraph(rtBucketPtr)) - return constLt && constRt - && constLt->getSExtValue() == constRt->getSExtValue(); - - if (!inGraph(ltBucketPtr)) { - std::swap(lt, rt); - std::swap(constLt, constRt); - std::swap(ltBucketPtr, rtBucketPtr); - } - - if (!inGraph(rtBucketPtr)) { - assert(inGraph(ltBucketPtr)); - C ltEqual = getEqualConstant(ltBucketPtr); - if (!constRt || !ltEqual) - return false; - return constRt->getSExtValue() == ltEqual->getSExtValue(); - } - - assert(inGraph(ltBucketPtr) && inGraph(rtBucketPtr)); - return isEqual(ltBucketPtr, rtBucketPtr); - } - - bool isNonEqual(T lt, T rt) const { - - C constLt = llvm::dyn_cast(lt); - C constRt = llvm::dyn_cast(rt); - - EqualityBucket* ltBucketPtr = getBucket(lt); - EqualityBucket* rtBucketPtr = getBucket(rt); - - if (!inGraph(ltBucketPtr) && !inGraph(rtBucketPtr)) - return constLt && constRt - && constLt->getSExtValue() != constRt->getSExtValue(); - - if (!inGraph(ltBucketPtr)) { - std::swap(lt, rt); - std::swap(constLt, constRt); - std::swap(ltBucketPtr, rtBucketPtr); - } - - if (!inGraph(rtBucketPtr)) { - assert (inGraph(ltBucketPtr)); - C ltEqual = getEqualConstant(ltBucketPtr); - if (!constRt || !ltEqual) - return false; - return constRt->getSExtValue() != ltEqual->getSExtValue(); - } - - assert(inGraph(ltBucketPtr) && inGraph(rtBucketPtr)); - return isNonEqual(ltBucketPtr, rtBucketPtr); - } - - bool isLesser(T lt, T rt) const { - - C constLt = llvm::dyn_cast(lt); - C constRt = llvm::dyn_cast(rt); - - EqualityBucket* ltBucketPtr = getBucket(lt); - EqualityBucket* rtBucketPtr = getBucket(rt); - - if (!inGraph(ltBucketPtr) && !inGraph(rtBucketPtr)) - return constLt && constRt - && constLt->getSExtValue() < constRt->getSExtValue(); - - if (!inGraph(rtBucketPtr)) { - C constBound = getUpperBound(ltBucketPtr, true); - if (constBound && constRt && constBound->getSExtValue() <= constRt->getSExtValue()) - return true; - constBound = getUpperBound(ltBucketPtr, false); - return constBound && constRt && constBound->getSExtValue() < constRt->getSExtValue(); - } - - if (!inGraph(ltBucketPtr)) { - C constBound = getLowerBound(rtBucketPtr, true); - if (constLt && constBound && constLt->getSExtValue() <= constBound->getSExtValue()) - return true; - constBound = getLowerBound(rtBucketPtr, false); - return constLt && constBound && constLt->getSExtValue() < constBound->getSExtValue(); - } - - assert (inGraph(ltBucketPtr) && inGraph(rtBucketPtr)); - return isLesser(ltBucketPtr, rtBucketPtr); - } - - bool isLesserEqual(T lt, T rt) const { - - C constLt = llvm::dyn_cast(lt); - C constRt = llvm::dyn_cast(rt); - - EqualityBucket* ltBucketPtr = getBucket(lt); - EqualityBucket* rtBucketPtr = getBucket(rt); - - if (!inGraph(ltBucketPtr) && !inGraph(rtBucketPtr)) - return constLt && constRt - && constLt->getSExtValue() <= constRt->getSExtValue(); - - if (!inGraph(rtBucketPtr)) { - C constBound = getUpperBound(ltBucketPtr, false); - return constBound && constRt && constBound->getSExtValue() <= constRt->getSExtValue(); - } - - if (!inGraph(ltBucketPtr)) { - C constBound = getLowerBound(rtBucketPtr, false); - return constLt && constBound && constLt->getSExtValue() <= constBound->getSExtValue(); - } - - assert (inGraph(ltBucketPtr) && inGraph(rtBucketPtr)); - return isLesserEqual(ltBucketPtr, rtBucketPtr); - } - - bool hasConflictingRelation(T lt, T rt, Relation relation) const { - switch (relation) { - case Relation::EQ: - return isNonEqual(lt, rt) - || isLesser(lt, rt) - || isLesser(rt, lt); - - case Relation::NE: - return isEqual(lt, rt); - - case Relation::LT: - return isLesserEqual(rt, lt); - - case Relation::LE: - return isLesser(rt, lt); - - case Relation::GT: - return hasConflictingRelation(rt, lt, Relation::LT); - - case Relation::GE: - return hasConflictingRelation(rt, lt, Relation::LE); - - case Relation::LOAD: - return hasLoad(rt) && inGraph(lt) - && hasConflictingRelation(mapToBucket.at(lt), loads.at(mapToBucket.at(rt)), Relation::EQ); - } - assert(0 && "unreachable"); - abort(); - } - - bool isLoad(T from, T val) const { - - EqualityBucket* fromBucketPtr = getBucket(from); - EqualityBucket* valBucketPtr = getBucket(val); - - if (!inGraph(fromBucketPtr) || !inGraph(valBucketPtr)) - return false; - - return isLoad(fromBucketPtr, valBucketPtr); - } - - bool hasLoad(T from) const { - - EqualityBucket* fromBucketPtr = getBucket(from); - - if (!inGraph(fromBucketPtr)) - return false; - - return hasLoad(fromBucketPtr); - } - - std::vector getEqual(T val) const { - std::vector result; - if (mapToBucket.find(val) == mapToBucket.end()) { - result.push_back(val); - return result; - } - - const EqualityBucket* valBucket = mapToBucket.at(val); - return valBucket->getEqual(); - } - - ValueIterator begin_lesser(T val) const { - return ValueIterator(mapToBucket.at(val), true, ValueIterator::Type::DOWN, true); - } - - ValueIterator end_lesser(T val) const { - return ValueIterator(mapToBucket.at(val), true, ValueIterator::Type::DOWN, false); - } - - ValueIterator begin_lesserEqual(T val) const { - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::DOWN, true); - } - - ValueIterator end_lesserEqual(T val) const { - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::DOWN, false); - } - - ValueIterator begin_greater(T val) const { - return ValueIterator(mapToBucket.at(val), true, ValueIterator::Type::UP, true); - } - - ValueIterator end_greater(T val) const { - return ValueIterator(mapToBucket.at(val), true, ValueIterator::Type::UP, false); - } - - ValueIterator begin_greaterEqual(T val) const { - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::UP, true); - } - - ValueIterator end_greaterEqual(T val) const { - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::UP, false); - } - - ValueIterator begin_all(T val) const { - // TODO add non-equal values - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::ALL, true); - } - - ValueIterator end_all(T val) const { - return ValueIterator(mapToBucket.at(val), false, ValueIterator::Type::ALL, false); - } - - std::vector getDirectlyLesser(T val) const { - return getDirectlyRelated(val, true); - } - - std::vector getDirectlyGreater(T val) const { - return getDirectlyRelated(val, false); - } - - std::vector getAllRelated(T val) const { - std::vector result; - for (auto it = begin_all(val); it != end_all(val); ++it) { - result.push_back((*it).first); - } - return result; - } - - C getLesserEqualBound(T val) const { - - if (!inGraph(val)) - return llvm::dyn_cast(val); - return getLowerBound(mapToBucket.at(val), false); - } - - C getGreaterEqualBound(T val) const { - - if (!inGraph(val)) - return llvm::dyn_cast(val); - return getUpperBound(mapToBucket.at(val), false); - } - - std::vector getAllValues() const { - std::vector result; - for (auto& pair : mapToBucket) - result.push_back(pair.first); - return result; - } - - std::vector getPtrsByVal(T val) { - if (!inGraph(val)) - return std::vector(); - EqualityBucket* valBucketPtr = mapToBucket.at(val); - - EqualityBucket* fromBucketPtr = findByValue(loads, valBucketPtr); - return fromBucketPtr ? fromBucketPtr->getEqual() : std::vector(); - } - - const std::vector getValsByPtr(T from) const { - if (!inGraph(from)) - return std::vector(); - EqualityBucket* fromBucketPtr = mapToBucket.at(from); - - EqualityBucket* valBucketPtr = findByKey(loads, fromBucketPtr); - return valBucketPtr ? valBucketPtr->getEqual() : std::vector(); - } - - std::set, std::vector>> getAllLoads() const { - std::set, std::vector>> result; - for (const auto& pair : loads) { - result.emplace(pair.first->getEqual(), pair.second->getEqual()); - } - return result; - } - - const std::vector& getValidAreas() const { - return validAreas; - } - - std::vector& getValidAreas() { - return validAreas; - } - - bool hasComparativeRelations(unsigned placeholder) const { - if (placeholderBuckets.find(placeholder) == placeholderBuckets.end()) - return false; - - return hasComparativeRelations(placeholderBuckets.at(placeholder)); - } - - unsigned newPlaceholderBucket() { - EqualityBucket* bucket = new EqualityBucket; - buckets.emplace_back(bucket); - placeholderBuckets.emplace(++lastPlaceholderId, bucket); - return lastPlaceholderId; - } - - void erasePlaceholderBucket(unsigned id) { - // DANGER erases bucket for good, not just - // the mention in placeholderBuckets - EqualityBucket* bucket = placeholderBuckets[id]; - - eraseUniquePtr(buckets, bucket); - placeholderBuckets.erase(id); - } - - bool holdsAnyRelations() const { - return !buckets.empty(); - } - -#ifndef NDEBUG - std::string strip(std::string str) const { - std::string result; - int space_counter = 0; - for (char c : str) { - if (c != ' ' || ++space_counter <= 2) { - result += c; - } else - break; - } - return result; - } - - void printVals(std::ostream& stream, const EqualityBucket* bucket) const { - stream << "{ "; - stream.flush(); - - for (auto pair : placeholderBuckets) { - if (pair.second == bucket) stream << "placeholder " << pair.first << " "; - } - - for (auto val : bucket->getEqual()) { - stream << strip(debug::getValName(val)) << "; "; - } - - stream << "}"; - } - - void printInterleaved(std::ostream& stream, const EqualityBucket* e1, - std::string sep, const EqualityBucket* e2) const { - printVals(stream, e1); - stream << sep; - printVals(stream, e2); - if (&stream == &std::cout) - stream << "\\r"; - else - stream << std::endl; - } - - void dump() { - generalDump(std::cout); - } - - void ddump() { - generalDump(std::cerr); - } - - void ddump(EqualityBucket* bucket, bool just = false) { - if (just) - printVals(std::cerr, bucket); - else - dump(std::cerr, bucket); - } - - void ddump(const llvm::Value* val) { - if (!inGraph(val)) - return; - - std::cerr << debug::getValName(val) << ":" << std::endl; - dump(std::cerr, mapToBucket.at(val)); - std::cerr << std::endl; - } - - void dump(std::ostream& stream, EqualityBucket* bucket) { - for (auto ptr : bucket->lesser) - printInterleaved(stream, ptr, " < ", bucket); - - for (auto ptr : bucket->lesserEqual) - printInterleaved(stream, ptr, " <= ", bucket); - - auto foundNonEqual = nonEqualities.find(bucket); - if (foundNonEqual != nonEqualities.end()) { - for (EqualityBucket* nonEqual : foundNonEqual->second) - if (nonEqual < bucket) - printInterleaved(stream, nonEqual, " != ", bucket); - } - - EqualityBucket* foundValue = findByKey(loads, bucket); - if (foundValue) - printInterleaved(stream, foundValue, " = LOAD ", bucket); - - if (bucket->lesser.empty() // values just equal and nothing else - && bucket->lesserEqual.empty() - && bucket->parents.empty() - && foundNonEqual == nonEqualities.end() - && !findByValue(loads, bucket) - && !foundValue) { - printVals(stream, bucket); - stream << std::endl; - } - } - - void generalDump(std::ostream& stream) { - - for (const auto& bucketPtr : buckets) { - dump(stream, bucketPtr.get()); - } - - } -#endif - -}; - -} // namespace vr -} // namespace dg - -#endif // DG_LLVM_RELATIONS_MAP_H_ diff --git a/include/dg/llvm/ValueRelations/getValName.h b/include/dg/llvm/ValueRelations/getValName.h deleted file mode 100644 index df719e6ac..000000000 --- a/include/dg/llvm/ValueRelations/getValName.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef DG_LLVM_GET_VAL_NAME_H_ -#define DG_LLVM_GET_VAL_NAME_H_ - -#include -#include -#include -#include -#include - -namespace dg{ -namespace debug { -inline std::string getValName(const llvm::Value *val) { - std::ostringstream ostr; - llvm::raw_os_ostream ro(ostr); - - assert(val); - ro << *val; - ro.flush(); - - std::string result = ostr.str(); - size_t start = result.find_first_not_of(" "); - assert (start != std::string::npos); - - // break the string if it is too long - return result.substr(start); -} - -inline std::string getTypeName(const llvm::Type* type) { - std::ostringstream ostr; - llvm::raw_os_ostream ro(ostr); - - assert(type); -#if LLVM_VERSION_MAJOR <= 4 - ro << *const_cast(type); -#else - ro << *type; -#endif - ro.flush(); - - std::string result = ostr.str(); - size_t start = result.find_first_not_of(" "); - assert (start != std::string::npos); - - // break the string if it is too long - return result.substr(start); -} - -} // namespace debug -} // namespace dg - -#endif // DG_LLVM_GET_VAL_NAME_H_ diff --git a/include/dg/llvm/llvm-utils.h b/include/dg/llvm/llvm-utils.h deleted file mode 100644 index 8aa3da7a2..000000000 --- a/include/dg/llvm/llvm-utils.h +++ /dev/null @@ -1,215 +0,0 @@ -#ifndef _DG_LLVM_UTILS_H_ -#define _DG_LLVM_UTILS_H_ - -#include -#include -#include - -namespace dg { -namespace llvmutils { - -using namespace llvm; - -/* ---------------------------------------------- - * -- PRINTING - * ---------------------------------------------- */ -inline void print(const Value *val, - raw_ostream& os, - const char *prefix=nullptr, - bool newline = false) -{ - if (prefix) - os << prefix; - - if (isa(val)) - os << val->getName().data(); - else - os << *val; - - if (newline) - os << "\n"; -} - -inline void printerr(const char *msg, const Value *val, bool newline = true) -{ - print(val, errs(), msg, newline); -} - -/* ---------------------------------------------- - * -- CASTING - * ---------------------------------------------- */ -inline bool isPointerOrIntegerTy(const Type *Ty) -{ - return Ty->isPointerTy() || Ty->isIntegerTy(); -} - -// can the given function be called by the given call inst? -enum class CallCompatibility { - STRICT, // require full compatibility - LOOSE, // ignore some incompatible patterns that usually work - // in practice, e.g., calling a function of 2 arguments - // with 3 arguments. - MATCHING_ARGS // check only that matching arguments are compatible, - // ignore the number of arguments, etc. -}; - -inline bool callIsCompatible(const Function *F, const CallInst *CI, - CallCompatibility policy = CallCompatibility::LOOSE) -{ - using namespace llvm; - - if (policy != CallCompatibility::MATCHING_ARGS) { - if (F->isVarArg()) { - if (F->arg_size() > CI->getNumArgOperands()) { - return false; - } - } else if (F->arg_size() != CI->getNumArgOperands()) { - if (policy == CallCompatibility::STRICT || - F->arg_size() > CI->getNumArgOperands()) { - // too few arguments - return false; - } - } - - if (!F->getReturnType()->canLosslesslyBitCastTo(CI->getType())) { - // it showed up that the loosless bitcast is too strict - // alternative since we can use the constexpr castings - if (!(isPointerOrIntegerTy(F->getReturnType()) && - isPointerOrIntegerTy(CI->getType()))) { - return false; - } - } - } - - size_t idx = 0; - auto max_idx = CI->getNumArgOperands(); - for (auto A = F->arg_begin(), E = F->arg_end(); - idx < max_idx && A != E; ++A, ++idx) { - Type *CTy = CI->getArgOperand(idx)->getType(); - Type *ATy = A->getType(); - - if (!(isPointerOrIntegerTy(CTy) && isPointerOrIntegerTy(ATy))) - if (!CTy->canLosslesslyBitCastTo(ATy)) { - return false; - } - } - - return true; -} - -/* ---------------------------------------------- - * -- analysis helpers - * ---------------------------------------------- */ - -inline unsigned getPointerBitwidth(const llvm::DataLayout *DL, - const llvm::Value *ptr) - -{ - const llvm::Type *Ty = ptr->getType(); - return DL->getPointerSizeInBits(Ty->getPointerAddressSpace()); -} - - - -inline uint64_t getConstantValue(const llvm::Value *op) -{ - using namespace llvm; - - //FIXME: we should get rid of this dependency - static_assert(sizeof(Offset::type) == sizeof(uint64_t), - "The code relies on Offset::type having 8 bytes"); - - uint64_t size = Offset::UNKNOWN; - if (const ConstantInt *C = dyn_cast(op)) { - size = C->getLimitedValue(); - } - - // size is ~((uint64_t)0) if it is unknown - return size; -} - - -// get size of memory allocation argument -inline uint64_t getConstantSizeValue(const llvm::Value *op) { - auto sz = getConstantValue(op); - // if the size is unknown, make it 0, so that pointer - // analysis correctly computes offets into this memory - // (which is always UNKNOWN) - if (sz == ~static_cast(0)) - return 0; - return sz; -} - -inline uint64_t getAllocatedSize(const llvm::AllocaInst *AI, - const llvm::DataLayout *DL) -{ - llvm::Type *Ty = AI->getAllocatedType(); - if (!Ty->isSized()) - return 0; - - if (AI->isArrayAllocation()) { - return getConstantSizeValue(AI->getArraySize()) * DL->getTypeAllocSize(Ty); - } else - return DL->getTypeAllocSize(Ty); -} - -inline uint64_t getAllocatedSize(llvm::Type *Ty, const llvm::DataLayout *DL) -{ - // Type can be i8 *null or similar - if (!Ty->isSized()) - return 0; - - return DL->getTypeAllocSize(Ty); -} - -inline bool isConstantZero(const llvm::Value *val) -{ - using namespace llvm; - - if (const ConstantInt *C = dyn_cast(val)) - return C->isZero(); - - return false; -} - -/* ---------------------------------------------- - * -- pointer analysis helpers - * ---------------------------------------------- */ -inline bool memsetIsZeroInitialization(const llvm::IntrinsicInst *I) -{ - return isConstantZero(I->getOperand(1)); -} - -// recursively find out if type contains a pointer type as a subtype -// (or if it is a pointer type itself) -inline bool tyContainsPointer(const llvm::Type *Ty) -{ - if (Ty->isAggregateType()) { - for (auto I = Ty->subtype_begin(), E = Ty->subtype_end(); - I != E; ++I) { - if (tyContainsPointer(*I)) - return true; - } - } else - return Ty->isPointerTy(); - - return false; -} - -inline bool typeCanBePointer(const llvm::DataLayout *DL, llvm::Type *Ty) -{ - if (Ty->isPointerTy()) - return true; - - if (Ty->isIntegerTy() && Ty->isSized()) - return DL->getTypeSizeInBits(Ty) - >= DL->getPointerSizeInBits(/*Ty->getPointerAddressSpace()*/); - - return false; -} - -} // namespace llvmutils -} // namespace dg - -#endif // _DG_LLVM_UTILS_H_ - diff --git a/include/dg/util/SilenceLLVMWarnings.h b/include/dg/util/SilenceLLVMWarnings.h deleted file mode 100644 index 5e39a3059..000000000 --- a/include/dg/util/SilenceLLVMWarnings.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef DG_SILENCE_LLVM_WARNINGS_H -#define DG_SILENCE_LLVM_WARNINGS_H - -// Helper macros to ignore unrelated warnings in LLVM libraries - -#ifndef SILENCE_LLVM_WARNINGS_PUSH -#if defined(_MSC_VER) -#define SILENCE_LLVM_WARNINGS_PUSH \ -_Pragma("warning(push)"); -// TODO -#elif defined(__clang__) -#define SILENCE_LLVM_WARNINGS_PUSH \ -_Pragma("clang diagnostic push"); \ -_Pragma("clang diagnostic ignored \"-Wunused-parameter\""); -#else // GCC -#define SILENCE_LLVM_WARNINGS_PUSH \ -_Pragma("GCC diagnostic push"); \ -_Pragma("GCC diagnostic ignored \"-Wunused-parameter\""); -#endif -#endif - -#ifndef SILENCE_LLVM_WARNINGS_POP -#if defined(_MSC_VER) -#define SILENCE_LLVM_WARNINGS_POP \ -_Pragma("warning(pop)"); -#elif defined(__clang__) -#define SILENCE_LLVM_WARNINGS_POP \ -_Pragma("clang diagnostic pop"); -#else // GCC -#define SILENCE_LLVM_WARNINGS_POP \ -_Pragma("GCC diagnostic pop"); -#endif -#endif - -#endif // DG_SILENCE_LLVM_WARNINGS_H \ No newline at end of file diff --git a/include/dg/util/cow_shared_ptr.h b/include/dg/util/cow_shared_ptr.h deleted file mode 100644 index 9425aae15..000000000 --- a/include/dg/util/cow_shared_ptr.h +++ /dev/null @@ -1,80 +0,0 @@ -#ifndef _COW_SHARED_PTR_H_ -#define _COW_SHARED_PTR_H_ - -#include - -/// -// Shared pointer with copy-on-write support -template -class cow_shared_ptr : public std::shared_ptr { - // am I the owner of the copy? - bool owner{true}; - - public: - cow_shared_ptr() = default; - cow_shared_ptr(T *p) : std::shared_ptr(p) {} - cow_shared_ptr(cow_shared_ptr&&) = delete; - cow_shared_ptr(const cow_shared_ptr& rhs) - : std::shared_ptr(rhs), owner(false) {} - - void reset(T *p) { - owner = true; - std::shared_ptr::reset(p); - } - - const T *get() const { return std::shared_ptr::get(); } - const T *operator->() const { return get(); } - const T *operator*() const { return get(); } - - T *getWritable() { - if (owner) - return std::shared_ptr::get(); - - // create a copy of the object and claim the ownership - assert(!owner); - if (get() != nullptr) { - reset(new T(*get())); - } else { - reset(new T()); - } - assert(owner); // set in reset() method - return std::shared_ptr::get(); - } -}; - -/* -template -class cow_shared_ptr { - std::shared_ptr ptr{nullptr}; - // am I the owner of the copy? - bool owner{false}; - - public: - cow_shared_ptr() = default; - cow_shared_ptr(T *p) : ptr(p), owner(true) {} - cow_shared_ptr(cow_shared_ptr&&) = delete; - cow_shared_ptr(const cow_shared_ptr& rhs) - : ptr(rhs.ptr), owner(false) {} - - const T *get() const { return ptr.get(); } - - T *getWritable() { - if (owner) - return ptr.get(); - // create a copy of the object and claim the ownership - if (ptr) { - ptr.reset(new T(*get())); - } else { - ptr.reset(new T()); - } - owner = true; - return ptr.get(); - } - - auto use_count() const -> decltype(ptr.use_count()) { - return ptr.use_count(); - } -}; -*/ - -#endif // _COW_SHARED_PTR_H_ diff --git a/include/dg/util/debug.h b/include/dg/util/debug.h deleted file mode 100644 index 1d47bf750..000000000 --- a/include/dg/util/debug.h +++ /dev/null @@ -1,117 +0,0 @@ -#ifndef DG_UTIL_DEBUG_H_ -#define DG_UTIL_DEBUG_H_ - -#ifdef DEBUG_ENABLED -#include -#include -#include -#endif - -namespace dg { -namespace debug { - -#ifdef DEBUG_ENABLED - -extern unsigned _debug_lvl; -extern unsigned _ind; - -namespace { -static inline unsigned& _getDebugLvl() { - return _debug_lvl; -} - -static inline void _setDebugLvl(unsigned int x) { - _getDebugLvl() = x; -} - -static inline unsigned& _getInd() { - return _ind; -} - -static inline std::ostream& _stream() { - return std::cerr; -} - -static void _dump_ind() { - auto ind = _getInd(); - for (unsigned i = 0; i < ind; ++i) - _stream() << " "; -} - -static void _dump_prefix(const char *domain, const char *add = nullptr) { - std::cerr << "[" << std::clock() << "]"; - if (domain) - _stream() << "[" << domain << "]"; - - _stream() << " "; - - _dump_ind(); - if (add) { - _stream() << add; - } -} -} - -/* -static inline bool dbg_should_print(unsigned int x) { - return _getDebugLvl() > x; -} -*/ - -inline void dbg_enable() { - _setDebugLvl(1); -} - -inline bool dbg_is_enabled() { - return _getDebugLvl() > 0; -} - -inline std::ostream& dbg_section_begin(const char *domain = nullptr) { - _dump_prefix(domain, "-> "); - _getInd() += 3; - return _stream(); -} - -inline std::ostream& dbg_section_end(const char *domain = nullptr) { - assert(_getInd() >= 3); - _getInd() -= 3; - _dump_prefix(domain, "<- "); - return _stream(); -} - -inline std::ostream& dbg(const char *domain = nullptr) { - _dump_prefix(domain); - return _stream(); -} - -#define DBG_ENABLE() do { ::dg::debug::dbg_enable(); } while(0) - -#define DBG_SECTION_BEGIN(dom, S)\ - do { if (::dg::debug::dbg_is_enabled()) {\ - ::dg::debug::dbg_section_begin((#dom)) << S << "\n"; }\ - } while(0) - -#define DBG_SECTION_END(dom, S)\ - do { if (::dg::debug::dbg_is_enabled()) {\ - ::dg::debug::dbg_section_end((#dom)) << S << "\n"; }\ - } while(0) - -#define DBG(dom, S)\ - do { if (::dg::debug::dbg_is_enabled()) {\ - ::dg::debug::dbg((#dom)) << S << "\n"; }\ - } while(0) - -#else // not DEBUG_ENABLED - -#define DBG_ENABLE() -#define DBG_SECTION_BEGIN(dom, S) -#define DBG_SECTION_END(dom, S) -#define DBG(dom, S) - -#endif // DEBUG_ENABLED - -} // namespace debug -} // namespace dg - - -#endif // DG_UTIL_DEBUG_H_ diff --git a/include/dg/util/iterators.h b/include/dg/util/iterators.h deleted file mode 100644 index 3f620f03c..000000000 --- a/include/dg/util/iterators.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef DG_ITERATORS_UTILS_H_ -#define DG_ITERATORS_UTILS_H_ - -namespace dg { - -template -struct iterator_filter : public It { - It _current; - It _end; - const Predicate& pred; - - iterator_filter(const It& b, const It& e, const Predicate& p) : _current(b), _end(e), pred(p) {} - template - iterator_filter(Range& r, const Predicate& p) : _current(r.begin()), _end(r.end()), pred(p) {} - - iterator_filter& operator++() { - It::operator++(); - while (_current != _end && !pred(It::operator*())) { - It::operator++(); - } - return *this; - } - - iterator_filter operator++(int) { - auto tmp = *this; - operator++(); - return tmp; - } - -}; - -} // namespace dg - -#endif