svf-tools 1.0.1189 → 1.0.1191

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "svf-tools",
3
- "version": "1.0.1189",
3
+ "version": "1.0.1191",
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": {
@@ -218,26 +218,55 @@ private:
218
218
  *
219
219
  * @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles
220
220
  */
221
- virtual void handleCycleWTO(const ICFGCycleWTO* cycle);
221
+ virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle);
222
222
 
223
- void handleWTOComponents(const std::list<const ICFGWTOComp*>& wtoComps);
223
+ /**
224
+ * Handle a function using worklist algorithm
225
+ *
226
+ * @param funEntry The entry node of the function to handle
227
+ */
228
+ void handleFunction(const ICFGNode* funEntry);
224
229
 
225
- void handleWTOComponent(const ICFGWTOComp* wtoComp);
230
+ /**
231
+ * Handle an ICFG node by merging states and processing statements
232
+ *
233
+ * @param node The ICFG node to handle
234
+ * @return true if state changed, false if fixpoint reached or infeasible
235
+ */
236
+ bool handleICFGNode(const ICFGNode* node);
226
237
 
238
+ /**
239
+ * Get the next nodes of a node within the same function
240
+ *
241
+ * @param node The node to get successors for
242
+ * @return Vector of successor nodes
243
+ */
244
+ std::vector<const ICFGNode*> getNextNodes(const ICFGNode* node) const;
227
245
 
228
246
  /**
229
- * handle SVF Statement like CmpStmt, CallStmt, GepStmt, LoadStmt, StoreStmt, etc.
247
+ * Get the next nodes outside a cycle
230
248
  *
231
- * @param stmt SVFStatement which is a value flow of instruction
249
+ * @param cycle The cycle to get exit successors for
250
+ * @return Vector of successor nodes outside the cycle
232
251
  */
233
- virtual void handleSVFStatement(const SVFStmt* stmt);
252
+ std::vector<const ICFGNode*> getNextNodesOfCycle(const ICFGCycleWTO* cycle) const;
234
253
 
235
254
  /**
236
- * Check if this callnode is recursive call and skip it.
255
+ * Recursively collect cycle heads from nested WTO components
237
256
  *
238
- * @param callnode CallICFGNode which calls a recursive function
257
+ * @param comps The list of WTO components to collect cycle heads from
239
258
  */
240
- virtual void SkipRecursiveCall(const CallICFGNode* callnode);
259
+ void collectCycleHeads(const std::list<const ICFGWTOComp*>& comps);
260
+
261
+
262
+ /**
263
+ * handle SVF Statement like CmpStmt, CallStmt, GepStmt, LoadStmt, StoreStmt, etc.
264
+ *
265
+ * @param stmt SVFStatement which is a value flow of instruction
266
+ */
267
+ virtual void handleSVFStatement(const SVFStmt* stmt);
268
+
269
+ virtual void setTopToObjInRecursion(const CallICFGNode* callnode);
241
270
 
242
271
 
243
272
  /**
@@ -299,6 +328,7 @@ private:
299
328
  Map<const FunObjVar*, const ICFGWTO*> funcToWTO;
300
329
  Set<std::pair<const CallICFGNode*, NodeID>> nonRecursiveCallSites;
301
330
  Set<const FunObjVar*> recursiveFuns;
331
+ Map<const ICFGNode*, const ICFGCycleWTO*> cycleHeadToCycle;
302
332
 
303
333
 
304
334
  bool hasAbsStateFromTrace(const ICFGNode* node)
@@ -313,15 +343,16 @@ private:
313
343
 
314
344
  // helper functions in handleCallSite
315
345
  virtual bool isExtCall(const CallICFGNode* callNode);
316
- virtual void extCallPass(const CallICFGNode* callNode);
346
+ virtual void handleExtCall(const CallICFGNode* callNode);
317
347
  virtual bool isRecursiveFun(const FunObjVar* fun);
318
348
  virtual bool isRecursiveCall(const CallICFGNode* callNode);
319
349
  virtual void recursiveCallPass(const CallICFGNode *callNode);
320
350
  virtual bool isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar *);
321
- virtual bool isDirectCall(const CallICFGNode* callNode);
322
- virtual void directCallFunPass(const CallICFGNode* callNode);
323
- virtual bool isIndirectCall(const CallICFGNode* callNode);
324
- virtual void indirectCallFunPass(const CallICFGNode* callNode);
351
+ virtual void handleFunCall(const CallICFGNode* callNode);
352
+
353
+ bool skipRecursiveCall(const CallICFGNode* callNode);
354
+ const FunObjVar* getCallee(const CallICFGNode* callNode);
355
+ bool shouldApplyNarrowing(const FunObjVar* fun);
325
356
 
326
357
  // there data should be shared with subclasses
327
358
  Map<std::string, std::function<void(const CallICFGNode*)>> func_map;
@@ -73,14 +73,25 @@ AbstractInterpretation::~AbstractInterpretation()
73
73
  }
74
74
 
75
75
  /**
76
- * @brief Compute WTO for each function partition entry
76
+ * @brief Recursively collect cycle heads from nested WTO components
77
77
  *
78
- * This function first identifies function partition entries (pair: <entry, function set>),
79
- * and then compute the IWTO for each pair.
80
- * It does this by detecting call graph's strongly connected components (SCC).
81
- * Each SCC forms a function partition, and any function that is invoked from outside its SCC
82
- * is identified as an entry of the function partition.
78
+ * This helper function traverses the WTO component tree and builds the cycleHeadToCycle
79
+ * map, which maps each cycle head node to its corresponding ICFGCycleWTO object.
80
+ * This enables efficient O(1) lookup of cycles during analysis.
83
81
  */
82
+ void AbstractInterpretation::collectCycleHeads(const std::list<const ICFGWTOComp*>& comps)
83
+ {
84
+ for (const ICFGWTOComp* comp : comps)
85
+ {
86
+ if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
87
+ {
88
+ cycleHeadToCycle[cycle->head()->getICFGNode()] = cycle;
89
+ // Recursively collect nested cycle heads
90
+ collectCycleHeads(cycle->getWTOComponents());
91
+ }
92
+ }
93
+ }
94
+
84
95
  void AbstractInterpretation::initWTO()
85
96
  {
86
97
  AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(svfir);
@@ -142,6 +153,13 @@ void AbstractInterpretation::initWTO()
142
153
  funcToWTO[it->second->getFunction()] = iwto;
143
154
  }
144
155
  }
156
+
157
+ // Build cycleHeadToCycle map for all functions
158
+ // This maps cycle head nodes to their corresponding WTO cycles for efficient lookup
159
+ for (auto& [func, wto] : funcToWTO)
160
+ {
161
+ collectCycleHeads(wto->getWTOComponents());
162
+ }
145
163
  }
146
164
 
147
165
  /// Program entry
@@ -154,8 +172,9 @@ void AbstractInterpretation::analyse()
154
172
  icfg->getGlobalICFGNode())[PAG::getPAG()->getBlkPtr()] = IntervalValue::top();
155
173
  if (const CallGraphNode* cgn = svfir->getCallGraph()->getCallGraphNode("main"))
156
174
  {
157
- const ICFGWTO* wto = funcToWTO[cgn->getFunction()];
158
- handleWTOComponents(wto->getWTOComponents());
175
+ // Use worklist-based function handling instead of recursive WTO component handling
176
+ const ICFGNode* mainEntry = icfg->getFunEntryICFGNode(cgn->getFunction());
177
+ handleFunction(mainEntry);
159
178
  }
160
179
  }
161
180
 
@@ -215,21 +234,12 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode * icfgNo
215
234
  else if (const RetCFGEdge *retCfgEdge =
216
235
  SVFUtil::dyn_cast<RetCFGEdge>(edge))
217
236
  {
218
- switch (Options::HandleRecur())
219
- {
220
- case TOP:
237
+ // Only include return edge if the corresponding callsite was processed
238
+ // (skipped recursive callsites in WIDEN_ONLY/WIDEN_NARROW won't have state)
239
+ const RetICFGNode* retNode = SVFUtil::dyn_cast<RetICFGNode>(icfgNode);
240
+ if (hasAbsStateFromTrace(retNode->getCallICFGNode()))
221
241
  {
222
242
  workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]);
223
- break;
224
- }
225
- case WIDEN_ONLY:
226
- case WIDEN_NARROW:
227
- {
228
- const RetICFGNode* returnSite = SVFUtil::dyn_cast<RetICFGNode>(icfgNode);
229
- const CallICFGNode* callSite = returnSite->getCallICFGNode();
230
- if (hasAbsStateFromTrace(callSite))
231
- workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]);
232
- }
233
243
  }
234
244
  }
235
245
  else
@@ -577,56 +587,228 @@ void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSing
577
587
  }
578
588
 
579
589
  /**
580
- * @brief Handle two types of WTO components (singleton and cycle)
590
+ * Handle an ICFG node by merging states from predecessors and processing statements
591
+ * Returns true if the abstract state has changed, false if fixpoint reached or infeasible
581
592
  */
582
- void AbstractInterpretation::handleWTOComponents(const std::list<const ICFGWTOComp*>& wtoComps)
593
+ bool AbstractInterpretation::handleICFGNode(const ICFGNode* node)
583
594
  {
584
- for (const ICFGWTOComp* wtoNode : wtoComps)
595
+ // Store the previous state for fixpoint detection
596
+ AbstractState prevState;
597
+ bool hadPrevState = hasAbsStateFromTrace(node);
598
+ if (hadPrevState)
599
+ prevState = abstractTrace[node];
600
+
601
+ // For function entry nodes, initialize state from caller or global
602
+ bool isFunEntry = SVFUtil::isa<FunEntryICFGNode>(node);
603
+ if (isFunEntry)
604
+ {
605
+ // Try to merge from predecessors first (handles call edges)
606
+ if (!mergeStatesFromPredecessors(node))
607
+ {
608
+ // No predecessors with state - initialize from caller or global
609
+ if (!callSiteStack.empty())
610
+ {
611
+ // Get state from the most recent call site
612
+ const CallICFGNode* caller = callSiteStack.back();
613
+ if (hasAbsStateFromTrace(caller))
614
+ {
615
+ abstractTrace[node] = abstractTrace[caller];
616
+ }
617
+ else
618
+ {
619
+ abstractTrace[node] = AbstractState();
620
+ }
621
+ }
622
+ else
623
+ {
624
+ // This is the main function entry, inherit from global node
625
+ const ICFGNode* globalNode = icfg->getGlobalICFGNode();
626
+ if (hasAbsStateFromTrace(globalNode))
627
+ {
628
+ abstractTrace[node] = abstractTrace[globalNode];
629
+ }
630
+ else
631
+ {
632
+ abstractTrace[node] = AbstractState();
633
+ }
634
+ }
635
+ }
636
+ }
637
+ else
585
638
  {
586
- handleWTOComponent(wtoNode);
639
+ // Merge states from predecessors
640
+ if (!mergeStatesFromPredecessors(node))
641
+ return false;
587
642
  }
643
+
644
+ stat->getBlockTrace()++;
645
+ stat->getICFGNodeTrace()++;
646
+
647
+ // Handle SVF statements
648
+ for (const SVFStmt *stmt: node->getSVFStmts())
649
+ {
650
+ handleSVFStatement(stmt);
651
+ }
652
+
653
+ // Handle call sites
654
+ if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
655
+ {
656
+ handleCallSite(callNode);
657
+ }
658
+
659
+ // Run detectors
660
+ for (auto& detector: detectors)
661
+ detector->detect(getAbsStateFromTrace(node), node);
662
+ stat->countStateSize();
663
+
664
+ // Check if state changed (for fixpoint detection)
665
+ // For entry nodes on first visit, always return true to process successors
666
+ if (isFunEntry && !hadPrevState)
667
+ return true;
668
+
669
+ if (abstractTrace[node] == prevState)
670
+ return false;
671
+
672
+ return true;
588
673
  }
589
674
 
590
- void AbstractInterpretation::handleWTOComponent(const ICFGWTOComp* wtoNode)
675
+ /**
676
+ * Get the next nodes of a node within the same function
677
+ * For CallICFGNode, includes shortcut to RetICFGNode
678
+ */
679
+ std::vector<const ICFGNode*> AbstractInterpretation::getNextNodes(const ICFGNode* node) const
591
680
  {
592
- if (const ICFGSingletonWTO* node = SVFUtil::dyn_cast<ICFGSingletonWTO>(wtoNode))
681
+ std::vector<const ICFGNode*> outEdges;
682
+ for (const ICFGEdge* edge : node->getOutEdges())
593
683
  {
594
- if (mergeStatesFromPredecessors(node->getICFGNode()))
595
- handleSingletonWTO(node);
684
+ const ICFGNode* dst = edge->getDstNode();
685
+ // Only nodes inside the same function are included
686
+ if (dst->getFun() == node->getFun())
687
+ {
688
+ outEdges.push_back(dst);
689
+ }
596
690
  }
597
- // Handle WTO cycles
598
- else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast<ICFGCycleWTO>(wtoNode))
691
+ if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
599
692
  {
600
- if (mergeStatesFromPredecessors(cycle->head()->getICFGNode()))
601
- handleCycleWTO(cycle);
693
+ // Shortcut to the RetICFGNode
694
+ const ICFGNode* retNode = callNode->getRetICFGNode();
695
+ outEdges.push_back(retNode);
602
696
  }
603
- // Assert false for unknown WTO types
604
- else
605
- assert(false && "unknown WTO type!");
697
+ return outEdges;
606
698
  }
607
699
 
608
- void AbstractInterpretation::handleCallSite(const ICFGNode* node)
700
+ /**
701
+ * Get the next nodes outside a cycle
702
+ * Inner cycles are skipped since their next nodes cannot be outside the outer cycle
703
+ */
704
+ std::vector<const ICFGNode*> AbstractInterpretation::getNextNodesOfCycle(const ICFGCycleWTO* cycle) const
609
705
  {
610
- if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
706
+ Set<const ICFGNode*> cycleNodes;
707
+ // Insert the head of the cycle and all component nodes
708
+ cycleNodes.insert(cycle->head()->getICFGNode());
709
+ for (const ICFGWTOComp* comp : cycle->getWTOComponents())
611
710
  {
612
- if (isExtCall(callNode))
711
+ if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
613
712
  {
614
- extCallPass(callNode);
713
+ cycleNodes.insert(singleton->getICFGNode());
615
714
  }
616
- else if (isRecursiveCall(callNode) && Options::HandleRecur() == TOP)
715
+ else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
617
716
  {
618
- recursiveCallPass(callNode);
717
+ cycleNodes.insert(subCycle->head()->getICFGNode());
718
+ }
719
+ }
720
+
721
+ std::vector<const ICFGNode*> outEdges;
722
+
723
+ // Check head's successors
724
+ std::vector<const ICFGNode*> nextNodes = getNextNodes(cycle->head()->getICFGNode());
725
+ for (const ICFGNode* nextNode : nextNodes)
726
+ {
727
+ // Only nodes that point outside the cycle are included
728
+ if (cycleNodes.find(nextNode) == cycleNodes.end())
729
+ {
730
+ outEdges.push_back(nextNode);
731
+ }
732
+ }
733
+
734
+ // Check each component's successors
735
+ for (const ICFGWTOComp* comp : cycle->getWTOComponents())
736
+ {
737
+ if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
738
+ {
739
+ std::vector<const ICFGNode*> compNextNodes = getNextNodes(singleton->getICFGNode());
740
+ for (const ICFGNode* nextNode : compNextNodes)
741
+ {
742
+ if (cycleNodes.find(nextNode) == cycleNodes.end())
743
+ {
744
+ outEdges.push_back(nextNode);
745
+ }
746
+ }
619
747
  }
620
- else if (isDirectCall(callNode))
748
+ // Skip inner cycles - they are handled within the outer cycle
749
+ }
750
+ return outEdges;
751
+ }
752
+
753
+ /**
754
+ * Handle a function using worklist algorithm
755
+ * This replaces the recursive WTO component handling with explicit worklist iteration
756
+ */
757
+ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry)
758
+ {
759
+ FIFOWorkList<const ICFGNode*> worklist;
760
+ worklist.push(funEntry);
761
+
762
+ while (!worklist.empty())
763
+ {
764
+ const ICFGNode* node = worklist.pop();
765
+
766
+ // Check if this node is a cycle head
767
+ if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end())
768
+ {
769
+ const ICFGCycleWTO* cycle = cycleHeadToCycle[node];
770
+ handleLoopOrRecursion(cycle);
771
+
772
+ // Push nodes outside the cycle to the worklist
773
+ std::vector<const ICFGNode*> cycleNextNodes = getNextNodesOfCycle(cycle);
774
+ for (const ICFGNode* nextNode : cycleNextNodes)
775
+ {
776
+ worklist.push(nextNode);
777
+ }
778
+ }
779
+ else
621
780
  {
622
- directCallFunPass(callNode);
781
+ // Handle regular node
782
+ if (!handleICFGNode(node))
783
+ {
784
+ // Fixpoint reached or infeasible, skip successors
785
+ continue;
786
+ }
787
+
788
+ // Push successor nodes to the worklist
789
+ std::vector<const ICFGNode*> nextNodes = getNextNodes(node);
790
+ for (const ICFGNode* nextNode : nextNodes)
791
+ {
792
+ worklist.push(nextNode);
793
+ }
623
794
  }
624
- else if (isIndirectCall(callNode))
795
+ }
796
+ }
797
+
798
+
799
+ void AbstractInterpretation::handleCallSite(const ICFGNode* node)
800
+ {
801
+ if (const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node))
802
+ {
803
+ if (isExtCall(callNode))
625
804
  {
626
- indirectCallFunPass(callNode);
805
+ handleExtCall(callNode);
627
806
  }
628
807
  else
629
- assert(false && "implement this part");
808
+ {
809
+ // Handle both direct and indirect calls uniformly
810
+ handleFunCall(callNode);
811
+ }
630
812
  }
631
813
  else
632
814
  assert (false && "it is not call node");
@@ -637,7 +819,7 @@ bool AbstractInterpretation::isExtCall(const CallICFGNode *callNode)
637
819
  return SVFUtil::isExtCall(callNode->getCalledFunction());
638
820
  }
639
821
 
640
- void AbstractInterpretation::extCallPass(const CallICFGNode *callNode)
822
+ void AbstractInterpretation::handleExtCall(const CallICFGNode *callNode)
641
823
  {
642
824
  callSiteStack.push_back(callNode);
643
825
  utils->handleExtAPI(callNode);
@@ -648,11 +830,13 @@ void AbstractInterpretation::extCallPass(const CallICFGNode *callNode)
648
830
  callSiteStack.pop_back();
649
831
  }
650
832
 
833
+ /// Check if a function is recursive (part of a call graph SCC)
651
834
  bool AbstractInterpretation::isRecursiveFun(const FunObjVar* fun)
652
835
  {
653
836
  return recursiveFuns.find(fun) != recursiveFuns.end();
654
837
  }
655
838
 
839
+ /// Check if a call node calls a recursive function
656
840
  bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode)
657
841
  {
658
842
  const FunObjVar *callfun = callNode->getCalledFunction();
@@ -662,10 +846,11 @@ bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode)
662
846
  return isRecursiveFun(callfun);
663
847
  }
664
848
 
849
+ /// Handle recursive call in TOP mode: set all stores and return value to TOP
665
850
  void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode)
666
851
  {
667
852
  AbstractState& as = getAbsStateFromTrace(callNode);
668
- SkipRecursiveCall(callNode);
853
+ setTopToObjInRecursion(callNode);
669
854
  const RetICFGNode *retNode = callNode->getRetICFGNode();
670
855
  if (retNode->getSVFStmts().size() > 0)
671
856
  {
@@ -681,6 +866,7 @@ void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode)
681
866
  abstractTrace[retNode] = as;
682
867
  }
683
868
 
869
+ /// Check if a call is a recursive callsite (within same SCC, not entry call from outside)
684
870
  bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode,
685
871
  const FunObjVar* callee)
686
872
  {
@@ -688,174 +874,219 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode,
688
874
  nonRecursiveCallSites.end();
689
875
  }
690
876
 
691
- bool AbstractInterpretation::isDirectCall(const CallICFGNode *callNode)
877
+ /// Get callee function: directly for direct calls, via pointer analysis for indirect calls
878
+ const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode)
692
879
  {
693
- const FunObjVar *callfun =callNode->getCalledFunction();
694
- if (!callfun)
695
- return false;
696
- else
697
- return !callfun->isDeclaration();
880
+ // Direct call: get callee directly from call node
881
+ if (const FunObjVar* callee = callNode->getCalledFunction())
882
+ return callee;
883
+
884
+ // Indirect call: resolve callee through pointer analysis
885
+ const auto callsiteMaps = svfir->getIndirectCallsites();
886
+ auto it = callsiteMaps.find(callNode);
887
+ if (it == callsiteMaps.end())
888
+ return nullptr;
889
+
890
+ NodeID call_id = it->second;
891
+ if (!hasAbsStateFromTrace(callNode))
892
+ return nullptr;
893
+
894
+ AbstractState& as = getAbsStateFromTrace(callNode);
895
+ if (!as.inVarToAddrsTable(call_id))
896
+ return nullptr;
897
+
898
+ AbstractValue Addrs = as[call_id];
899
+ if (Addrs.getAddrs().empty())
900
+ return nullptr;
901
+
902
+ NodeID addr = *Addrs.getAddrs().begin();
903
+ SVFVar* func_var = svfir->getGNode(as.getIDFromAddr(addr));
904
+ return SVFUtil::dyn_cast<FunObjVar>(func_var);
698
905
  }
699
- void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode)
906
+
907
+ /// Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped
908
+ bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode)
700
909
  {
701
- AbstractState& as = getAbsStateFromTrace(callNode);
910
+ const FunObjVar* callee = getCallee(callNode);
911
+ if (!callee)
912
+ return false;
702
913
 
703
- abstractTrace[callNode] = as;
914
+ // Non-recursive function: never skip, always inline
915
+ if (!isRecursiveFun(callee))
916
+ return false;
704
917
 
705
- const FunObjVar *calleeFun =callNode->getCalledFunction();
706
- if (Options::HandleRecur() == WIDEN_ONLY || Options::HandleRecur() == WIDEN_NARROW)
707
- {
708
- // If this CallICFGNode is a recursive callsite (i.e. this Node
709
- // resides in a recursive function 'fun' and its callee function is
710
- // in the same SCC with the fun), then skip it. Since the callee
711
- // function is handled during the handling of WTO of the whole recursion.
712
- if (isRecursiveCallSite(callNode, calleeFun))
713
- return;
714
- }
715
- else
918
+ // For recursive functions, skip only recursive callsites (within same SCC).
919
+ // Entry calls (from outside SCC) are not skipped - they are inlined so that
920
+ // handleLoopOrRecursion() can analyze the function body.
921
+ // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW).
922
+ return isRecursiveCallSite(callNode, callee);
923
+ }
924
+
925
+ /// Check if narrowing should be applied: always for regular loops, mode-dependent for recursion
926
+ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun)
927
+ {
928
+ // Non-recursive functions (regular loops): always apply narrowing
929
+ if (!isRecursiveFun(fun))
930
+ return true;
931
+
932
+ // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not
933
+ // TOP mode exits early in handleLoopOrRecursion, so should not reach here
934
+ switch (Options::HandleRecur())
716
935
  {
717
- // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions,
718
- // thus should not reach this branch
719
- assert(false && "Recursion mode TOP should not reach here!");
936
+ case TOP:
937
+ assert(false && "TOP mode should not reach narrowing phase for recursive functions");
938
+ return false;
939
+ case WIDEN_ONLY:
940
+ return false; // Skip narrowing for recursive functions
941
+ case WIDEN_NARROW:
942
+ return true; // Apply narrowing for recursive functions
943
+ default:
944
+ assert(false && "Unknown recursion handling mode");
945
+ return false;
720
946
  }
947
+ }
948
+ /// Handle direct or indirect call: get callee, process function body, set return state
949
+ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode)
950
+ {
951
+ AbstractState& as = getAbsStateFromTrace(callNode);
952
+ abstractTrace[callNode] = as;
953
+
954
+ // Skip recursive callsites (within SCC); entry calls are not skipped
955
+ if (skipRecursiveCall(callNode))
956
+ return;
957
+
958
+ const FunObjVar* callee = getCallee(callNode);
959
+ if (!callee)
960
+ return;
721
961
 
722
962
  callSiteStack.push_back(callNode);
723
963
 
724
- const ICFGWTO* wto = funcToWTO[calleeFun];
725
- handleWTOComponents(wto->getWTOComponents());
964
+ const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee);
965
+ handleFunction(calleeEntry);
726
966
 
727
967
  callSiteStack.pop_back();
728
- // handle Ret node
729
- const RetICFGNode *retNode = callNode->getRetICFGNode();
730
- // resume ES to callnode
968
+ const RetICFGNode* retNode = callNode->getRetICFGNode();
731
969
  abstractTrace[retNode] = abstractTrace[callNode];
732
970
  }
733
971
 
734
- bool AbstractInterpretation::isIndirectCall(const CallICFGNode *callNode)
972
+ /// Handle WTO cycle (loop or recursive function) using widening/narrowing iteration.
973
+ ///
974
+ /// Widening is applied at the cycle head to ensure termination of the analysis.
975
+ /// The cycle head's abstract state is iteratively updated until a fixpoint is reached.
976
+ ///
977
+ /// == What is being widened ==
978
+ /// The abstract state at the cycle head node, which includes:
979
+ /// - Variable values (intervals) that may change across loop iterations
980
+ /// - For example, a loop counter `i` starting at 0 and incrementing each iteration
981
+ ///
982
+ /// == Regular loops (non-recursive functions) ==
983
+ /// All modes (TOP/WIDEN_ONLY/WIDEN_NARROW) behave the same for regular loops:
984
+ /// 1. Widening phase: Iterate until the cycle head state stabilizes
985
+ /// Example: for(i=0; i<100; i++) -> i widens to [0, +inf]
986
+ /// 2. Narrowing phase: Refine the over-approximation from widening
987
+ /// Example: [0, +inf] narrows to [0, 100] using loop condition
988
+ ///
989
+ /// == Recursive function cycles ==
990
+ /// Behavior depends on Options::HandleRecur():
991
+ ///
992
+ /// - TOP mode:
993
+ /// Does not iterate. Calls recursiveCallPass() to set all stores and
994
+ /// return value to TOP immediately. This is the most conservative but fastest.
995
+ /// Example:
996
+ /// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
997
+ /// factorial(5) -> returns [-inf, +inf]
998
+ ///
999
+ /// - WIDEN_ONLY mode:
1000
+ /// Widening phase only, no narrowing for recursive functions.
1001
+ /// The recursive function body is analyzed with widening until fixpoint.
1002
+ /// Example:
1003
+ /// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
1004
+ /// factorial(5) -> returns [10000, +inf] (widened upper bound)
1005
+ ///
1006
+ /// - WIDEN_NARROW mode:
1007
+ /// Both widening and narrowing phases for recursive functions.
1008
+ /// After widening reaches fixpoint, narrowing refines the result.
1009
+ /// Example:
1010
+ /// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); }
1011
+ /// factorial(5) -> returns [10000, 10000] (precise after narrowing)
1012
+ void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle)
735
1013
  {
736
- const auto callsiteMaps = svfir->getIndirectCallsites();
737
- return callsiteMaps.find(callNode) != callsiteMaps.end();
738
- }
739
-
740
- void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode)
741
- {
742
- AbstractState& as = getAbsStateFromTrace(callNode);
743
- const auto callsiteMaps = svfir->getIndirectCallsites();
744
- NodeID call_id = callsiteMaps.at(callNode);
745
- if (!as.inVarToAddrsTable(call_id))
746
- {
747
- return;
748
- }
749
- AbstractValue Addrs = as[call_id];
750
- NodeID addr = *Addrs.getAddrs().begin();
751
- SVFVar *func_var = svfir->getGNode(as.getIDFromAddr(addr));
1014
+ const ICFGNode* cycle_head = cycle->head()->getICFGNode();
752
1015
 
753
- if(const FunObjVar* funObjVar = SVFUtil::dyn_cast<FunObjVar>(func_var))
1016
+ // TOP mode for recursive function cycles: use recursiveCallPass to set
1017
+ // all stores and return value to TOP, maintaining original semantics
1018
+ if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
754
1019
  {
755
- if (Options::HandleRecur() == WIDEN_ONLY || Options::HandleRecur() == WIDEN_NARROW)
1020
+ // Get the call node from callSiteStack (the call that entered this function)
1021
+ if (!callSiteStack.empty())
756
1022
  {
757
- if (isRecursiveCallSite(callNode, funObjVar))
758
- return;
1023
+ const CallICFGNode* callNode = callSiteStack.back();
1024
+ recursiveCallPass(callNode);
759
1025
  }
760
-
761
- const FunObjVar* callfun = funObjVar->getFunction();
762
- callSiteStack.push_back(callNode);
763
- abstractTrace[callNode] = as;
764
-
765
- const ICFGWTO* wto = funcToWTO[callfun];
766
- handleWTOComponents(wto->getWTOComponents());
767
- callSiteStack.pop_back();
768
- // handle Ret node
769
- const RetICFGNode* retNode = callNode->getRetICFGNode();
770
- abstractTrace[retNode] = abstractTrace[callNode];
1026
+ return;
771
1027
  }
772
- }
773
1028
 
774
- /// handle wto cycle (loop)
775
- void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO*cycle)
776
- {
777
- const ICFGNode* cycle_head = cycle->head()->getICFGNode();
778
- // Flag to indicate if we are in the increasing phase
1029
+ // WIDEN_ONLY / WIDEN_NARROW modes (and regular loops): iterate until fixpoint
779
1030
  bool increasing = true;
780
- // Infinite loop until a fixpoint is reached,
1031
+ u32_t widen_delay = Options::WidenDelay();
1032
+
781
1033
  for (u32_t cur_iter = 0;; cur_iter++)
782
1034
  {
783
- // Start widening or narrowing if cur_iter >= widen threshold (widen delay)
784
- if (cur_iter >= Options::WidenDelay())
1035
+ // Get the abstract state before processing the cycle head
1036
+ AbstractState prev_head_state;
1037
+ if (hasAbsStateFromTrace(cycle_head))
1038
+ prev_head_state = abstractTrace[cycle_head];
1039
+
1040
+ // Process the cycle head node
1041
+ handleICFGNode(cycle_head);
1042
+ AbstractState cur_head_state = abstractTrace[cycle_head];
1043
+
1044
+ // Start widening or narrowing if cur_iter >= widen delay threshold
1045
+ if (cur_iter >= widen_delay)
785
1046
  {
786
- // Widen or narrow after processing cycle head node
787
- AbstractState prev_head_state = abstractTrace[cycle_head];
788
- handleWTOComponent(cycle->head());
789
- AbstractState cur_head_state = abstractTrace[cycle_head];
790
1047
  if (increasing)
791
1048
  {
792
-
793
- if (isRecursiveFun(cycle->head()->getICFGNode()->getFun()) &&
794
- !(Options::HandleRecur() == WIDEN_ONLY ||
795
- Options::HandleRecur() == WIDEN_NARROW))
796
- {
797
- // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions,
798
- // thus should not reach this branch
799
- assert(false && "Recursion mode TOP should not reach here!");
800
- }
801
-
802
- // Widening
1049
+ // Apply widening operator
803
1050
  abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state);
804
1051
 
805
1052
  if (abstractTrace[cycle_head] == prev_head_state)
806
1053
  {
1054
+ // Widening fixpoint reached, switch to narrowing phase
807
1055
  increasing = false;
808
1056
  continue;
809
1057
  }
810
1058
  }
811
1059
  else
812
1060
  {
813
- // Narrowing, use different modes for nodes within recursions
814
- if (isRecursiveFun(cycle->head()->getICFGNode()->getFun()))
1061
+ // Narrowing phase - check if narrowing should be applied
1062
+ if (!shouldApplyNarrowing(cycle_head->getFun()))
815
1063
  {
816
- // For nodes in recursions, skip narrowing in WIDEN_TOP mode
817
- if (Options::HandleRecur() == WIDEN_ONLY)
818
- {
819
- break;
820
- }
821
- // Perform normal narrowing in WIDEN_NARROW mode
822
- else if (Options::HandleRecur() == WIDEN_NARROW)
823
- {
824
- // Widening's fixpoint reached in the widening phase, switch to narrowing
825
- abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
826
- if (abstractTrace[cycle_head] == prev_head_state)
827
- {
828
- // Narrowing's fixpoint reached in the narrowing phase, exit loop
829
- break;
830
- }
831
- }
832
- // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions,
833
- // thus should not reach this branch
834
- else
835
- {
836
- assert(false && "Recursion mode TOP should not reach here");
837
- }
1064
+ break;
838
1065
  }
839
- // For nodes outside recursions, perform normal narrowing
840
- else
1066
+
1067
+ // Apply narrowing
1068
+ abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
1069
+ if (abstractTrace[cycle_head] == prev_head_state)
841
1070
  {
842
- // Widening's fixpoint reached in the widening phase, switch to narrowing
843
- abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state);
844
- if (abstractTrace[cycle_head] == prev_head_state)
845
- {
846
- // Narrowing's fixpoint reached in the narrowing phase, exit loop
847
- break;
848
- }
1071
+ // Narrowing fixpoint reached, exit loop
1072
+ break;
849
1073
  }
850
1074
  }
851
1075
  }
852
- else
1076
+
1077
+ // Process cycle body components
1078
+ for (const ICFGWTOComp* comp : cycle->getWTOComponents())
853
1079
  {
854
- // Handle the cycle head
855
- handleWTOComponent(cycle->head());
1080
+ if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
1081
+ {
1082
+ handleICFGNode(singleton->getICFGNode());
1083
+ }
1084
+ else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
1085
+ {
1086
+ // Handle nested cycle recursively
1087
+ handleLoopOrRecursion(subCycle);
1088
+ }
856
1089
  }
857
- // Handle the cycle body
858
- handleWTOComponents(cycle->getWTOComponents());
859
1090
  }
860
1091
  }
861
1092
 
@@ -920,7 +1151,8 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt)
920
1151
  !getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr());
921
1152
  }
922
1153
 
923
- void AbstractInterpretation::SkipRecursiveCall(const CallICFGNode *callNode)
1154
+ /// Set all store values in a recursive function to TOP (used in TOP mode)
1155
+ void AbstractInterpretation::setTopToObjInRecursion(const CallICFGNode *callNode)
924
1156
  {
925
1157
  AbstractState& as = getAbsStateFromTrace(callNode);
926
1158
  const RetICFGNode *retNode = callNode->getRetICFGNode();