svf-tools 1.0.1223 → 1.0.1225

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/package.json +1 -1
  2. package/svf/include/AE/Svfexe/AbstractInterpretation.h +5 -4
  3. package/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +90 -205
  4. package/SVF-doxygen/doxygen.config +0 -2548
  5. package/SVF-doxygen/wiki/PAG.png +0 -0
  6. package/SVF-doxygen/wiki/andersen.png +0 -0
  7. package/SVF-doxygen/wiki/callgraph.png +0 -0
  8. package/SVF-doxygen/wiki/consG.png +0 -0
  9. package/SVF-doxygen/wiki/cpu2000-flto +0 -432
  10. package/SVF-doxygen/wiki/cpu2006-flto +0 -417
  11. package/SVF-doxygen/wiki/cpu2017-wllvm.cfg +0 -999
  12. package/SVF-doxygen/wiki/database.png +0 -0
  13. package/SVF-doxygen/wiki/framework.png +0 -0
  14. package/SVF-doxygen/wiki/help.png +0 -0
  15. package/SVF-doxygen/wiki/icfg.png +0 -0
  16. package/SVF-doxygen/wiki/mssa-cha.png +0 -0
  17. package/SVF-doxygen/wiki/pagedge.png +0 -0
  18. package/SVF-doxygen/wiki/pagnode.png +0 -0
  19. package/SVF-doxygen/wiki/pt.png +0 -0
  20. package/SVF-doxygen/wiki/setupcmake.png +0 -0
  21. package/SVF-doxygen/wiki/setupconfiguration.png +0 -0
  22. package/SVF-doxygen/wiki/setupdashboard.png +0 -0
  23. package/SVF-doxygen/wiki/setupdebug.png +0 -0
  24. package/SVF-doxygen/wiki/setupenv.png +0 -0
  25. package/SVF-doxygen/wiki/startup.png +0 -0
  26. package/SVF-doxygen/wiki/svf-stat.pdf +0 -0
  27. package/SVF-doxygen/wiki/svfg-framework.png +0 -0
  28. package/SVF-doxygen/wiki/svfg.png +0 -0
  29. package/SVF-doxygen/wiki/svfg_opt.png +0 -0
  30. package/SVF-doxygen/wiki/svfgedge-cha.png +0 -0
  31. package/SVF-doxygen/wiki/svfgnode-cha.png +0 -0
  32. package/SVF-doxygen/wiki/svfpic/README.md +0 -6
  33. package/SVF-doxygen/wiki/svfpic/ass-1debug1.png +0 -0
  34. package/SVF-doxygen/wiki/svfpic/ass-1debug2.png +0 -0
  35. package/SVF-doxygen/wiki/svfpic/build.jpg +0 -0
  36. package/SVF-doxygen/wiki/svfpic/cmd.png +0 -0
  37. package/SVF-doxygen/wiki/svfpic/connect1.jpg +0 -0
  38. package/SVF-doxygen/wiki/svfpic/connect2.png +0 -0
  39. package/SVF-doxygen/wiki/svfpic/connect3.png +0 -0
  40. package/SVF-doxygen/wiki/svfpic/connect4.jpg +0 -0
  41. package/SVF-doxygen/wiki/svfpic/connect5.jpg +0 -0
  42. package/SVF-doxygen/wiki/svfpic/connect6.png +0 -0
  43. package/SVF-doxygen/wiki/svfpic/connect7.jpg +0 -0
  44. package/SVF-doxygen/wiki/svfpic/continue.png +0 -0
  45. package/SVF-doxygen/wiki/svfpic/debug-new.png +0 -0
  46. package/SVF-doxygen/wiki/svfpic/debug-new2.png +0 -0
  47. package/SVF-doxygen/wiki/svfpic/debug1.jpeg +0 -0
  48. package/SVF-doxygen/wiki/svfpic/debug2.jpeg +0 -0
  49. package/SVF-doxygen/wiki/svfpic/debug3.png +0 -0
  50. package/SVF-doxygen/wiki/svfpic/debug4.png +0 -0
  51. package/SVF-doxygen/wiki/svfpic/debug5.jpeg +0 -0
  52. package/SVF-doxygen/wiki/svfpic/debug6.jpeg +0 -0
  53. package/SVF-doxygen/wiki/svfpic/docker_sys_requirement.png +0 -0
  54. package/SVF-doxygen/wiki/svfpic/docker_sys_requirements.png +0 -0
  55. package/SVF-doxygen/wiki/svfpic/dockerbuild.png +0 -0
  56. package/SVF-doxygen/wiki/svfpic/dockerbuild2.jpg +0 -0
  57. package/SVF-doxygen/wiki/svfpic/dockerbuild3.jpg +0 -0
  58. package/SVF-doxygen/wiki/svfpic/dockerbuild4.png +0 -0
  59. package/SVF-doxygen/wiki/svfpic/dockerbuild5.jpg +0 -0
  60. package/SVF-doxygen/wiki/svfpic/dockerbuildimage.png +0 -0
  61. package/SVF-doxygen/wiki/svfpic/dockercmd.png +0 -0
  62. package/SVF-doxygen/wiki/svfpic/dockercmd2.png +0 -0
  63. package/SVF-doxygen/wiki/svfpic/dockercontainer.png +0 -0
  64. package/SVF-doxygen/wiki/svfpic/dockerdb1.jpg +0 -0
  65. package/SVF-doxygen/wiki/svfpic/dockerdb10.jpeg +0 -0
  66. package/SVF-doxygen/wiki/svfpic/dockerdb2.jpg +0 -0
  67. package/SVF-doxygen/wiki/svfpic/dockerdb3.jpg +0 -0
  68. package/SVF-doxygen/wiki/svfpic/dockerdb4.jpg +0 -0
  69. package/SVF-doxygen/wiki/svfpic/dockerdb5.png +0 -0
  70. package/SVF-doxygen/wiki/svfpic/dockerdb6.jpeg +0 -0
  71. package/SVF-doxygen/wiki/svfpic/dockerdb7.png +0 -0
  72. package/SVF-doxygen/wiki/svfpic/dockerdb8.png +0 -0
  73. package/SVF-doxygen/wiki/svfpic/dockerdb9.jpeg +0 -0
  74. package/SVF-doxygen/wiki/svfpic/dockerfinshbuilt.png +0 -0
  75. package/SVF-doxygen/wiki/svfpic/dockerimage.png +0 -0
  76. package/SVF-doxygen/wiki/svfpic/dockernameImage.png +0 -0
  77. package/SVF-doxygen/wiki/svfpic/dockerpull.png +0 -0
  78. package/SVF-doxygen/wiki/svfpic/dockerpull2.png +0 -0
  79. package/SVF-doxygen/wiki/svfpic/download.jpg +0 -0
  80. package/SVF-doxygen/wiki/svfpic/extension1.jpeg +0 -0
  81. package/SVF-doxygen/wiki/svfpic/extension2.jpeg +0 -0
  82. package/SVF-doxygen/wiki/svfpic/graphviz.png +0 -0
  83. package/SVF-doxygen/wiki/svfpic/hellodb.png +0 -0
  84. package/SVF-doxygen/wiki/svfpic/hellodb2.png +0 -0
  85. package/SVF-doxygen/wiki/svfpic/hviz_0.png +0 -0
  86. package/SVF-doxygen/wiki/svfpic/hviz_1.png +0 -0
  87. package/SVF-doxygen/wiki/svfpic/hviz_2.png +0 -0
  88. package/SVF-doxygen/wiki/svfpic/installC:C++Ext.png +0 -0
  89. package/SVF-doxygen/wiki/svfpic/installCMakeExt.png +0 -0
  90. package/SVF-doxygen/wiki/svfpic/installRCext.png +0 -0
  91. package/SVF-doxygen/wiki/svfpic/installdockerext.png +0 -0
  92. package/SVF-doxygen/wiki/svfpic/launch1.png +0 -0
  93. package/SVF-doxygen/wiki/svfpic/openfile.png +0 -0
  94. package/SVF-doxygen/wiki/svfpic/pathfolder.png +0 -0
  95. package/SVF-doxygen/wiki/svfpic/restart.png +0 -0
  96. package/SVF-doxygen/wiki/svfpic/rundocker.png +0 -0
  97. package/SVF-doxygen/wiki/svfpic/runinCLI.png +0 -0
  98. package/SVF-doxygen/wiki/svfpic/screen.png +0 -0
  99. package/SVF-doxygen/wiki/svfpic/settings1.jpg +0 -0
  100. package/SVF-doxygen/wiki/svfpic/settings2.jpg +0 -0
  101. package/SVF-doxygen/wiki/svfpic/settings3.jpg +0 -0
  102. package/SVF-doxygen/wiki/svfpic/shortlists.png +0 -0
  103. package/SVF-doxygen/wiki/svfpic/start.png +0 -0
  104. package/SVF-doxygen/wiki/svfpic/start1.png +0 -0
  105. package/SVF-doxygen/wiki/svfpic/update0.png +0 -0
  106. package/SVF-doxygen/wiki/svfpic/verify_docker.png +0 -0
  107. package/SVF-doxygen/wiki/svfpic/vs_entry_window.png +0 -0
  108. package/SVF-doxygen/wiki/svfpic/wsl.png +0 -0
  109. package/SVF-doxygen/wiki/svfpic/wsl_1.png +0 -0
  110. package/SVF-doxygen/wiki/svfpic/wsl_2.png +0 -0
  111. package/SVF-doxygen/wiki/svfpic/wsl_3.png +0 -0
  112. package/SVF-doxygen/wiki/tools.png +0 -0
  113. package/SVF-doxygen/wiki/users.png +0 -0
  114. package/SVF-doxygen/wiki/vm1.png +0 -0
  115. package/SVF-doxygen/wiki/vm2.png +0 -0
  116. package/SVF-doxygen/wiki/vm3.png +0 -0
  117. package/SVF-doxygen/wiki/vm4.png +0 -0
  118. package/SVF-doxygen/wiki/vm5.png +0 -0
  119. package/SVF-doxygen/wiki/vscode_build_tasks.png +0 -0
  120. package/SVF-doxygen/wiki/vscode_cpp_extension.png +0 -0
  121. package/SVF-doxygen/wiki/vscode_debug_list.png +0 -0
  122. package/SVF-doxygen/wiki/vscode_dir_structure.png +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svf-tools",
3
- "version": "1.0.1223",
3
+ "version": "1.0.1225",
4
4
  "description": "* <b>[TypeClone](https://github.com/SVF-tools/SVF/wiki/TypeClone) published in our [ECOOP paper](https://yuleisui.github.io/publications/ecoop20.pdf) is now available in SVF </b> * <b>SVF now uses a single script for its build. Just type [`source ./build.sh`](https://github.com/SVF-tools/SVF/blob/master/build.sh) in your terminal, that's it!</b> * <b>SVF now supports LLVM-10.0.0! </b> * <b>We thank [bsauce](https://github.com/bsauce) for writing a user manual of SVF ([link1](https://www.jianshu.com/p/068a08ec749c) and [link2](https://www.jianshu.com/p/777c30d4240e)) in Chinese </b> * <b>SVF now supports LLVM-9.0.0 (Thank [Byoungyoung Lee](https://github.com/SVF-tools/SVF/issues/142) for his help!). </b> * <b>SVF now supports a set of [field-sensitive pointer analyses](https://yuleisui.github.io/publications/sas2019a.pdf). </b> * <b>[Use SVF as an external lib](https://github.com/SVF-tools/SVF/wiki/Using-SVF-as-a-lib-in-your-own-tool) for your own project (Contributed by [Hongxu Chen](https://github.com/HongxuChen)). </b> * <b>SVF now supports LLVM-7.0.0. </b> * <b>SVF now supports Docker. [Try SVF in Docker](https://github.com/SVF-tools/SVF/wiki/Try-SVF-in-Docker)! </b> * <b>SVF now supports [LLVM-6.0.0](https://github.com/svf-tools/SVF/pull/38) (Contributed by [Jack Anthony](https://github.com/jackanth)). </b> * <b>SVF now supports [LLVM-4.0.0](https://github.com/svf-tools/SVF/pull/23) (Contributed by Jared Carlson. Thank [Jared](https://github.com/jcarlson23) and [Will](https://github.com/dtzWill) for their in-depth [discussions](https://github.com/svf-tools/SVF/pull/18) about updating SVF!) </b> * <b>SVF now supports analysis for C++ programs.</b> <br />",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -156,9 +156,10 @@ private:
156
156
  /// Initialize abstract state for the global ICFG node and process global statements
157
157
  virtual void handleGlobalNode();
158
158
 
159
- /// Propagate the post-state of a node to all its intra-procedural successors
160
- void propagateToSuccessor(const ICFGNode* node,
161
- const Set<const ICFGNode*>* withinSet = nullptr);
159
+ /// Pull-based state merge: read abstractTrace[pred] for each predecessor,
160
+ /// apply branch refinement for conditional IntraCFGEdges, and join into
161
+ /// abstractTrace[node]. Returns true if at least one predecessor had state.
162
+ bool mergeStatesFromPredecessors(const ICFGNode* node);
162
163
 
163
164
  /// Check if the branch on intraEdge is feasible under abstract state as
164
165
  bool isBranchFeasible(const IntraCFGEdge* intraEdge, AbstractState& as);
@@ -242,7 +243,7 @@ private:
242
243
  // there data should be shared with subclasses
243
244
  Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
244
245
 
245
- Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states for nodes (pre-state before execution, post-state after)
246
+ Map<const ICFGNode*, AbstractState> abstractTrace; // abstract states for nodes
246
247
  Set<const ICFGNode*> allAnalyzedNodes; // All nodes ever analyzed (across all entry points)
247
248
  std::string moduleName;
248
249
 
@@ -286,61 +286,67 @@ void AbstractInterpretation::handleGlobalNode()
286
286
  AddressValue(BlackHoleObjAddr);
287
287
  }
288
288
 
289
- /// Propagate the post-state of a node to all its intra-procedural successors.
290
- /// For conditional branches, the state is refined along each outgoing edge via
291
- /// isBranchFeasible. The refined state is joined into the successor's abstractTrace entry.
292
- /// Call/return edges are handled explicitly by handleFunCall, not here.
293
- ///
294
- /// If withinSet is non-null, only propagate to successors contained in that set.
295
- /// This is used during cycle iteration to avoid propagating to nodes outside the
296
- /// cycle, which would cause stale accumulation and lose narrowing precision.
297
- /// Additionally, when withinSet is provided and a RetCFGEdge target is within the set,
298
- /// the state is also propagated along that edge. This handles the FunExit -> RetNode
299
- /// path inside recursive function cycles, where the recursive call is skipped and
300
- /// the RetCFGEdge effectively acts as intra-procedural control flow.
301
- void AbstractInterpretation::propagateToSuccessor(const ICFGNode* node,
302
- const Set<const ICFGNode*>* withinSet)
289
+ /// Pull-based state merge: for each predecessor that has an abstract state,
290
+ /// copy its state, apply branch refinement for conditional IntraCFGEdges,
291
+ /// and join all feasible states into abstractTrace[node].
292
+ /// Returns true if at least one predecessor contributed state.
293
+ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode* node)
303
294
  {
304
- if (!hasAbstractState(node))
305
- return;
306
-
307
- for (auto& edge : node->getOutEdges())
295
+ std::vector<AbstractState> workList;
296
+ for (auto& edge : node->getInEdges())
308
297
  {
309
- if (const IntraCFGEdge* intraEdge =
310
- SVFUtil::dyn_cast<IntraCFGEdge>(edge))
311
- {
312
- const ICFGNode* dst = intraEdge->getDstNode();
313
- // If a filter set is provided, skip successors not in the set
314
- if (withinSet && withinSet->find(dst) == withinSet->end())
315
- continue;
298
+ const ICFGNode* pred = edge->getSrcNode();
299
+ if (abstractTrace.find(pred) == abstractTrace.end())
300
+ continue;
316
301
 
317
- AbstractState state = abstractTrace[node];
318
- if (intraEdge->getCondition())
302
+ if (const IntraCFGEdge* intraCfgEdge = SVFUtil::dyn_cast<IntraCFGEdge>(edge))
303
+ {
304
+ AbstractState tmpState = abstractTrace[pred];
305
+ if (intraCfgEdge->getCondition())
319
306
  {
320
- if (!isBranchFeasible(intraEdge, state))
321
- continue; // infeasible branch, do not propagate
322
- // state has been refined in-place by isBranchFeasible
307
+ if (isBranchFeasible(intraCfgEdge, tmpState))
308
+ workList.push_back(tmpState);
323
309
  }
324
- if (hasAbstractState(dst))
325
- abstractTrace[dst].joinWith(state);
326
310
  else
327
- abstractTrace[dst] = state;
311
+ {
312
+ workList.push_back(tmpState);
313
+ }
314
+ }
315
+ else if (SVFUtil::isa<CallCFGEdge>(edge))
316
+ {
317
+ workList.push_back(abstractTrace[pred]);
328
318
  }
329
- else if (withinSet && SVFUtil::isa<RetCFGEdge>(edge))
319
+ else if (SVFUtil::isa<RetCFGEdge>(edge))
330
320
  {
331
- // In recursive cycles, FunExit and RetNode are both inside the cycle.
332
- // Propagate along RetCFGEdge when both endpoints are in the cycle.
333
- const ICFGNode* dst = edge->getDstNode();
334
- if (withinSet->find(dst) != withinSet->end())
321
+ switch (Options::HandleRecur())
335
322
  {
336
- if (hasAbstractState(dst))
337
- abstractTrace[dst].joinWith(abstractTrace[node]);
338
- else
339
- abstractTrace[dst] = abstractTrace[node];
323
+ case TOP:
324
+ {
325
+ workList.push_back(abstractTrace[pred]);
326
+ break;
327
+ }
328
+ case WIDEN_ONLY:
329
+ case WIDEN_NARROW:
330
+ {
331
+ const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(node);
332
+ const CallICFGNode* callSite = returnSite->getCallICFGNode();
333
+ if (hasAbstractState(callSite))
334
+ workList.push_back(abstractTrace[pred]);
335
+ break;
336
+ }
340
337
  }
341
338
  }
342
- // CallCFGEdge is handled by handleFunCall
343
339
  }
340
+
341
+ if (workList.empty())
342
+ return false;
343
+
344
+ auto it = workList.begin();
345
+ abstractTrace[node] = *it;
346
+ for (++it; it != workList.end(); ++it)
347
+ abstractTrace[node].joinWith(*it);
348
+
349
+ return true;
344
350
  }
345
351
 
346
352
 
@@ -642,9 +648,9 @@ bool AbstractInterpretation::isBranchFeasible(const IntraCFGEdge* intraEdge,
642
648
  return true;
643
649
  }
644
650
  /**
645
- * Handle an ICFG node: execute statements and propagate post-state to successors.
646
- * The node's pre-state must already be in abstractTrace (set by predecessors'
647
- * propagateToSuccessor, or by handleGlobalNode/handleFunction for entry nodes).
651
+ * Handle an ICFG node: execute statements on the current abstract state.
652
+ * The node's pre-state must already be in abstractTrace (set by
653
+ * mergeStatesFromPredecessors, or by handleGlobalNode for the global node).
648
654
  * Returns true if the abstract state has changed, false if fixpoint reached or unreachable.
649
655
  */
650
656
  bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
@@ -722,12 +728,13 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry, const Call
722
728
  if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
723
729
  {
724
730
  const ICFGNode* node = singleton->getICFGNode();
725
- handleICFGNode(node);
726
- propagateToSuccessor(node);
731
+ if (mergeStatesFromPredecessors(node))
732
+ handleICFGNode(node);
727
733
  }
728
734
  else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
729
735
  {
730
- handleLoopOrRecursion(cycle, caller);
736
+ if (mergeStatesFromPredecessors(cycle->head()->getICFGNode()))
737
+ handleLoopOrRecursion(cycle, caller);
731
738
  }
732
739
  }
733
740
  }
@@ -883,78 +890,36 @@ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
883
890
  {
884
891
  AbstractState& as = getAbstractState(callNode);
885
892
  abstractTrace[callNode] = as;
886
- // Skip recursive callsites (within SCC); entry calls are not skipped.
887
- // For skipped recursive calls, propagate the caller's state to the callee
888
- // entry as a back-edge contribution. This is needed because the IntraCFGEdge
889
- // from CallNode to RetNode was removed by the ICFG builder (replaced by
890
- // CallCFGEdge + RetCFGEdge), so propagateToSuccessor cannot propagate the
891
- // back-edge. We manually push the CallPE parameters to the callee entry.
892
893
  if (skipRecursiveCall(callNode))
893
- {
894
- const FunObjVar* callee = getCallee(callNode);
895
- const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
896
- // Push caller's state to callee entry (back-edge of recursive cycle)
897
- if (hasAbstractState(calleeEntry))
898
- abstractTrace[calleeEntry].joinWith(abstractTrace[callNode]);
899
- else
900
- abstractTrace[calleeEntry] = abstractTrace[callNode];
901
894
  return;
902
- }
903
895
 
904
896
  // Direct call: callee is known
905
897
  if (const FunObjVar* callee = callNode->getCalledFunction())
906
898
  {
907
899
  const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
908
- const ICFGNode* calleeExit = icfg->getFunExitICFGNode(callee);
909
- // Push caller's state to callee entry so CallPE can copy parameters
910
- abstractTrace[calleeEntry] = abstractTrace[callNode];
911
900
  handleFunction(calleeEntry, callNode);
912
- // Use callee exit state (which has memory side effects) for the return node
901
+ // Resume return node from caller's state (context-insensitive).
902
+ // Callee's side effects are reflected through shared abstractTrace.
913
903
  const RetICFGNode* retNode = callNode->getRetICFGNode();
914
- if (hasAbstractState(calleeExit))
915
- abstractTrace[retNode] = abstractTrace[calleeExit];
916
- else
917
- abstractTrace[retNode] = abstractTrace[callNode];
904
+ abstractTrace[retNode] = abstractTrace[callNode];
918
905
  return;
919
906
  }
920
907
 
921
908
  // Indirect call: use Andersen's call graph to get all resolved callees.
922
- // The call graph was built during PreAnalysis::initWTO() by running Andersen's pointer analysis,
923
- // which over-approximates the set of possible targets for each indirect callsite.
924
909
  const RetICFGNode* retNode = callNode->getRetICFGNode();
925
910
  if (callGraph->hasIndCSCallees(callNode))
926
911
  {
927
912
  const auto& callees = callGraph->getIndCSCallees(callNode);
928
- bool firstCallee = true;
929
913
  for (const FunObjVar* callee : callees)
930
914
  {
931
915
  if (callee->isDeclaration())
932
916
  continue;
933
917
  const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
934
- const ICFGNode* calleeExit = icfg->getFunExitICFGNode(callee);
935
- // Push caller's state to callee entry
936
- abstractTrace[calleeEntry] = abstractTrace[callNode];
937
918
  handleFunction(calleeEntry, callNode);
938
- // Use callee exit state for retNode (first callee assigns, rest join)
939
- if (hasAbstractState(calleeExit))
940
- {
941
- if (firstCallee)
942
- {
943
- abstractTrace[retNode] = abstractTrace[calleeExit];
944
- firstCallee = false;
945
- }
946
- else
947
- abstractTrace[retNode].joinWith(abstractTrace[calleeExit]);
948
- }
949
919
  }
950
- // If no callee was processed, fall back to caller's state
951
- if (firstCallee)
952
- abstractTrace[retNode] = abstractTrace[callNode];
953
- }
954
- else
955
- {
956
- abstractTrace[retNode] = abstractTrace[callNode];
957
920
  }
921
+ // Resume return node from caller's state (context-insensitive)
922
+ abstractTrace[retNode] = abstractTrace[callNode];
958
923
  }
959
924
 
960
925
  /// Handle WTO cycle (loop or recursive function) using widening/narrowing iteration.
@@ -1001,151 +966,71 @@ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle, co
1001
966
  {
1002
967
  const ICFGNode* cycle_head = cycle->head()->getICFGNode();
1003
968
 
1004
- // TOP mode for recursive function cycles: use handleRecursiveCall to set
1005
- // all stores and return value to TOP, maintaining original semantics
969
+ // TOP mode for recursive function cycles: set all stores and return value to TOP
1006
970
  if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
1007
971
  {
1008
972
  if (caller)
1009
- {
1010
973
  handleRecursiveCall(caller);
1011
- }
1012
974
  return;
1013
975
  }
1014
976
 
1015
- // Snapshot the external pre-state of the cycle head (contributions from
1016
- // outside the cycle, already propagated before we enter the cycle).
1017
- // This is fixed across all iterations; only back-edge contributions change.
1018
- AbstractState externalPre;
1019
- if (hasAbstractState(cycle_head))
1020
- externalPre = abstractTrace[cycle_head];
1021
-
1022
- // Collect all cycle body node pointers (excluding head) for clearing,
1023
- // and build cycleNodes set (head + body) for restricting propagation.
1024
- std::vector<const ICFGNode*> bodyNodes;
1025
- Set<const ICFGNode*> cycleNodes;
1026
- cycleNodes.insert(cycle_head);
1027
- for (const ICFGWTOComp* comp : cycle->getWTOComponents())
1028
- {
1029
- if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
1030
- {
1031
- bodyNodes.push_back(singleton->getICFGNode());
1032
- cycleNodes.insert(singleton->getICFGNode());
1033
- }
1034
- // Sub-cycle heads are handled recursively; their state is managed
1035
- // by their own handleLoopOrRecursion call, but we still need to
1036
- // clear them to avoid stale accumulation from previous outer iterations.
1037
- else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
1038
- {
1039
- bodyNodes.push_back(subCycle->head()->getICFGNode());
1040
- cycleNodes.insert(subCycle->head()->getICFGNode());
1041
- }
1042
- }
1043
-
1044
- // WIDEN_ONLY / WIDEN_NARROW modes (and regular loops): iterate until fixpoint.
1045
- // Widening/narrowing is applied to the head's PRE-state (before execution).
1046
- // Each iteration: widen/narrow pre-state -> execute head -> propagate ->
1047
- // execute body -> collect back-edge -> rebuild pre-state for next iteration.
977
+ // Iterate until fixpoint with widening/narrowing on the cycle head.
1048
978
  bool increasing = true;
1049
979
  u32_t widen_delay = Options::WidenDelay();
1050
- AbstractState prev_head_pre;
1051
- if (hasAbstractState(cycle_head))
1052
- prev_head_pre = abstractTrace[cycle_head];
1053
-
1054
980
  for (u32_t cur_iter = 0;; cur_iter++)
1055
981
  {
1056
- // Clear cycle body states to prevent stale accumulation from previous iteration.
1057
- for (const ICFGNode* bodyNode : bodyNodes)
1058
- abstractTrace.erase(bodyNode);
1059
-
1060
- // Apply widening or narrowing to the head's pre-state.
1061
- // Compare current pre-state with the previous iteration's pre-state.
1062
982
  if (cur_iter >= widen_delay)
1063
983
  {
1064
- AbstractState cur_head_pre = abstractTrace[cycle_head];
984
+ // Save state before processing head
985
+ AbstractState prev_head_state = abstractTrace[cycle_head];
986
+
987
+ // Process cycle head: merge from predecessors, then execute statements
988
+ // (uses same gated pattern as handleWTOComponent in origin/master)
989
+ if (mergeStatesFromPredecessors(cycle_head))
990
+ handleICFGNode(cycle_head);
991
+ AbstractState cur_head_state = abstractTrace[cycle_head];
992
+
1065
993
  if (increasing)
1066
994
  {
1067
- abstractTrace[cycle_head] = prev_head_pre.widening(cur_head_pre);
1068
- if (abstractTrace[cycle_head] == prev_head_pre)
995
+ abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state);
996
+ if (abstractTrace[cycle_head] == prev_head_state)
1069
997
  {
998
+ // Widening fixpoint reached; switch to narrowing phase.
1070
999
  increasing = false;
1071
- // Recompute with the stabilized state
1000
+ continue;
1072
1001
  }
1073
1002
  }
1074
1003
  else
1075
1004
  {
1076
1005
  if (!shouldApplyNarrowing(cycle_head->getFun()))
1077
1006
  break;
1078
-
1079
- abstractTrace[cycle_head] = prev_head_pre.narrowing(cur_head_pre);
1080
- if (abstractTrace[cycle_head] == prev_head_pre)
1007
+ abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
1008
+ if (abstractTrace[cycle_head] == prev_head_state)
1081
1009
  break;
1082
1010
  }
1083
1011
  }
1084
- // Save this iteration's pre-state for next iteration's comparison
1085
- prev_head_pre = abstractTrace[cycle_head];
1086
-
1087
- // Process the cycle head node (execute stmts, transforms pre to post)
1088
- handleICFGNode(cycle_head);
1089
-
1090
- // Propagate head's post-state to body successor nodes within the cycle,
1091
- // then erase head so the back-edge from body tail lands in a clean slot
1092
- // (not mixed with head's post-state).
1093
- propagateToSuccessor(cycle_head, &cycleNodes);
1094
- abstractTrace.erase(cycle_head);
1012
+ else
1013
+ {
1014
+ // Before widen_delay: process cycle head with gated pattern
1015
+ if (mergeStatesFromPredecessors(cycle_head))
1016
+ handleICFGNode(cycle_head);
1017
+ }
1095
1018
 
1096
- // Process cycle body components (propagation restricted to cycle nodes)
1019
+ // Process cycle body components (each with gated merge+handle)
1097
1020
  for (const ICFGWTOComp* comp : cycle->getWTOComponents())
1098
1021
  {
1099
1022
  if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
1100
1023
  {
1101
- const ICFGNode* bodyNode = singleton->getICFGNode();
1102
- handleICFGNode(bodyNode);
1103
- propagateToSuccessor(bodyNode, &cycleNodes);
1024
+ const ICFGNode* node = singleton->getICFGNode();
1025
+ if (mergeStatesFromPredecessors(node))
1026
+ handleICFGNode(node);
1104
1027
  }
1105
1028
  else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
1106
1029
  {
1107
- handleLoopOrRecursion(subCycle, caller);
1030
+ if (mergeStatesFromPredecessors(subCycle->head()->getICFGNode()))
1031
+ handleLoopOrRecursion(subCycle, caller);
1108
1032
  }
1109
1033
  }
1110
-
1111
- // After body processing, abstractTrace[cycle_head] holds only the
1112
- // back-edge contribution (if any). Rebuild head's pre-state for the
1113
- // next iteration: externalPre joined with back-edge.
1114
- if (hasAbstractState(cycle_head))
1115
- {
1116
- AbstractState backEdge = abstractTrace[cycle_head];
1117
- abstractTrace[cycle_head] = externalPre;
1118
- abstractTrace[cycle_head].joinWith(backEdge);
1119
- }
1120
- else
1121
- {
1122
- abstractTrace[cycle_head] = externalPre;
1123
- }
1124
- }
1125
-
1126
- // Final pass: re-execute all cycle nodes and propagate without restriction.
1127
- // During iterations, propagation was restricted to cycle-internal nodes.
1128
- // Now re-execute and propagate from all cycle nodes without restriction,
1129
- // so that exit edges (outside the cycle) receive the converged states.
1130
- // We must clear body nodes first and re-process them from scratch using
1131
- // the converged head pre-state, since body nodes may contain stale states
1132
- // from the last iteration that predate the final head re-execution.
1133
- for (const ICFGNode* bodyNode : bodyNodes)
1134
- abstractTrace.erase(bodyNode);
1135
- handleICFGNode(cycle_head);
1136
- propagateToSuccessor(cycle_head);
1137
- for (const ICFGWTOComp* comp : cycle->getWTOComponents())
1138
- {
1139
- if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
1140
- {
1141
- const ICFGNode* bodyNode = singleton->getICFGNode();
1142
- handleICFGNode(bodyNode);
1143
- propagateToSuccessor(bodyNode);
1144
- }
1145
- else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
1146
- {
1147
- handleLoopOrRecursion(subCycle, caller);
1148
- }
1149
1034
  }
1150
1035
  }
1151
1036