WorkList.cpp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. //===- WorkList.cpp - Analyzer work-list implementation--------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // Defines different worklist implementations for the static analyzer.
  10. //
  11. //===----------------------------------------------------------------------===//
  12. #include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
  13. #include "llvm/ADT/PriorityQueue.h"
  14. #include "llvm/ADT/DenseSet.h"
  15. #include "llvm/ADT/DenseMap.h"
  16. #include "llvm/ADT/STLExtras.h"
  17. #include "llvm/ADT/Statistic.h"
  18. #include <deque>
  19. #include <vector>
  20. using namespace clang;
  21. using namespace ento;
  22. #define DEBUG_TYPE "WorkList"
  23. STATISTIC(MaxQueueSize, "Maximum size of the worklist");
  24. STATISTIC(MaxReachableSize, "Maximum size of auxiliary worklist set");
  25. //===----------------------------------------------------------------------===//
  26. // Worklist classes for exploration of reachable states.
  27. //===----------------------------------------------------------------------===//
  28. namespace {
  29. class DFS : public WorkList {
  30. SmallVector<WorkListUnit, 20> Stack;
  31. public:
  32. bool hasWork() const override {
  33. return !Stack.empty();
  34. }
  35. void enqueue(const WorkListUnit& U) override {
  36. Stack.push_back(U);
  37. }
  38. WorkListUnit dequeue() override {
  39. assert(!Stack.empty());
  40. const WorkListUnit& U = Stack.back();
  41. Stack.pop_back(); // This technically "invalidates" U, but we are fine.
  42. return U;
  43. }
  44. };
  45. class BFS : public WorkList {
  46. std::deque<WorkListUnit> Queue;
  47. public:
  48. bool hasWork() const override {
  49. return !Queue.empty();
  50. }
  51. void enqueue(const WorkListUnit& U) override {
  52. Queue.push_back(U);
  53. }
  54. WorkListUnit dequeue() override {
  55. WorkListUnit U = Queue.front();
  56. Queue.pop_front();
  57. return U;
  58. }
  59. };
  60. } // namespace
  61. // Place the dstor for WorkList here because it contains virtual member
  62. // functions, and we the code for the dstor generated in one compilation unit.
  63. WorkList::~WorkList() = default;
  64. std::unique_ptr<WorkList> WorkList::makeDFS() {
  65. return std::make_unique<DFS>();
  66. }
  67. std::unique_ptr<WorkList> WorkList::makeBFS() {
  68. return std::make_unique<BFS>();
  69. }
  70. namespace {
  71. class BFSBlockDFSContents : public WorkList {
  72. std::deque<WorkListUnit> Queue;
  73. SmallVector<WorkListUnit, 20> Stack;
  74. public:
  75. bool hasWork() const override {
  76. return !Queue.empty() || !Stack.empty();
  77. }
  78. void enqueue(const WorkListUnit& U) override {
  79. if (U.getNode()->getLocation().getAs<BlockEntrance>())
  80. Queue.push_front(U);
  81. else
  82. Stack.push_back(U);
  83. }
  84. WorkListUnit dequeue() override {
  85. // Process all basic blocks to completion.
  86. if (!Stack.empty()) {
  87. const WorkListUnit& U = Stack.back();
  88. Stack.pop_back(); // This technically "invalidates" U, but we are fine.
  89. return U;
  90. }
  91. assert(!Queue.empty());
  92. // Don't use const reference. The subsequent pop_back() might make it
  93. // unsafe.
  94. WorkListUnit U = Queue.front();
  95. Queue.pop_front();
  96. return U;
  97. }
  98. };
  99. } // namespace
  100. std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() {
  101. return std::make_unique<BFSBlockDFSContents>();
  102. }
  103. namespace {
  104. class UnexploredFirstStack : public WorkList {
  105. /// Stack of nodes known to have statements we have not traversed yet.
  106. SmallVector<WorkListUnit, 20> StackUnexplored;
  107. /// Stack of all other nodes.
  108. SmallVector<WorkListUnit, 20> StackOthers;
  109. using BlockID = unsigned;
  110. using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
  111. llvm::DenseSet<LocIdentifier> Reachable;
  112. public:
  113. bool hasWork() const override {
  114. return !(StackUnexplored.empty() && StackOthers.empty());
  115. }
  116. void enqueue(const WorkListUnit &U) override {
  117. const ExplodedNode *N = U.getNode();
  118. auto BE = N->getLocation().getAs<BlockEntrance>();
  119. if (!BE) {
  120. // Assume the choice of the order of the preceding block entrance was
  121. // correct.
  122. StackUnexplored.push_back(U);
  123. } else {
  124. LocIdentifier LocId = std::make_pair(
  125. BE->getBlock()->getBlockID(),
  126. N->getLocationContext()->getStackFrame());
  127. auto InsertInfo = Reachable.insert(LocId);
  128. if (InsertInfo.second) {
  129. StackUnexplored.push_back(U);
  130. } else {
  131. StackOthers.push_back(U);
  132. }
  133. }
  134. MaxReachableSize.updateMax(Reachable.size());
  135. MaxQueueSize.updateMax(StackUnexplored.size() + StackOthers.size());
  136. }
  137. WorkListUnit dequeue() override {
  138. if (!StackUnexplored.empty()) {
  139. WorkListUnit &U = StackUnexplored.back();
  140. StackUnexplored.pop_back();
  141. return U;
  142. } else {
  143. WorkListUnit &U = StackOthers.back();
  144. StackOthers.pop_back();
  145. return U;
  146. }
  147. }
  148. };
  149. } // namespace
  150. std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() {
  151. return std::make_unique<UnexploredFirstStack>();
  152. }
  153. namespace {
  154. class UnexploredFirstPriorityQueue : public WorkList {
  155. using BlockID = unsigned;
  156. using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
  157. // How many times each location was visited.
  158. // Is signed because we negate it later in order to have a reversed
  159. // comparison.
  160. using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>;
  161. // Compare by number of times the location was visited first (negated
  162. // to prefer less often visited locations), then by insertion time (prefer
  163. // expanding nodes inserted sooner first).
  164. using QueuePriority = std::pair<int, unsigned long>;
  165. using QueueItem = std::pair<WorkListUnit, QueuePriority>;
  166. struct ExplorationComparator {
  167. bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
  168. return LHS.second < RHS.second;
  169. }
  170. };
  171. // Number of inserted nodes, used to emulate DFS ordering in the priority
  172. // queue when insertions are equal.
  173. unsigned long Counter = 0;
  174. // Number of times a current location was reached.
  175. VisitedTimesMap NumReached;
  176. // The top item is the largest one.
  177. llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
  178. queue;
  179. public:
  180. bool hasWork() const override {
  181. return !queue.empty();
  182. }
  183. void enqueue(const WorkListUnit &U) override {
  184. const ExplodedNode *N = U.getNode();
  185. unsigned NumVisited = 0;
  186. if (auto BE = N->getLocation().getAs<BlockEntrance>()) {
  187. LocIdentifier LocId = std::make_pair(
  188. BE->getBlock()->getBlockID(),
  189. N->getLocationContext()->getStackFrame());
  190. NumVisited = NumReached[LocId]++;
  191. }
  192. queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter)));
  193. }
  194. WorkListUnit dequeue() override {
  195. QueueItem U = queue.top();
  196. queue.pop();
  197. return U.first;
  198. }
  199. };
  200. } // namespace
  201. std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() {
  202. return std::make_unique<UnexploredFirstPriorityQueue>();
  203. }
  204. namespace {
  205. class UnexploredFirstPriorityLocationQueue : public WorkList {
  206. using LocIdentifier = const CFGBlock *;
  207. // How many times each location was visited.
  208. // Is signed because we negate it later in order to have a reversed
  209. // comparison.
  210. using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>;
  211. // Compare by number of times the location was visited first (negated
  212. // to prefer less often visited locations), then by insertion time (prefer
  213. // expanding nodes inserted sooner first).
  214. using QueuePriority = std::pair<int, unsigned long>;
  215. using QueueItem = std::pair<WorkListUnit, QueuePriority>;
  216. struct ExplorationComparator {
  217. bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
  218. return LHS.second < RHS.second;
  219. }
  220. };
  221. // Number of inserted nodes, used to emulate DFS ordering in the priority
  222. // queue when insertions are equal.
  223. unsigned long Counter = 0;
  224. // Number of times a current location was reached.
  225. VisitedTimesMap NumReached;
  226. // The top item is the largest one.
  227. llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
  228. queue;
  229. public:
  230. bool hasWork() const override {
  231. return !queue.empty();
  232. }
  233. void enqueue(const WorkListUnit &U) override {
  234. const ExplodedNode *N = U.getNode();
  235. unsigned NumVisited = 0;
  236. if (auto BE = N->getLocation().getAs<BlockEntrance>())
  237. NumVisited = NumReached[BE->getBlock()]++;
  238. queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter)));
  239. }
  240. WorkListUnit dequeue() override {
  241. QueueItem U = queue.top();
  242. queue.pop();
  243. return U.first;
  244. }
  245. };
  246. }
  247. std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() {
  248. return std::make_unique<UnexploredFirstPriorityLocationQueue>();
  249. }