From 6846eb2b4507f2434d3f15d2449caea3b33a9c2c Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 26 Jan 2026 22:18:33 +1100 Subject: [PATCH 01/18] refactor AE, use worklist algorithm(naive) --- .../AE/Svfexe/AbstractInterpretation.h | 32 ++ svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 312 ++++++++++++++++-- 2 files changed, 310 insertions(+), 34 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 53a76becf..e06322887 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -224,6 +224,37 @@ class AbstractInterpretation void handleWTOComponent(const ICFGWTOComp* wtoComp); + /** + * Handle a function using worklist algorithm + * + * @param funEntry The entry node of the function to handle + */ + void handleFunction(const ICFGNode* funEntry); + + /** + * Handle an ICFG node by merging states and processing statements + * + * @param node The ICFG node to handle + * @return true if state changed, false if fixpoint reached or infeasible + */ + bool handleICFGNode(const ICFGNode* node); + + /** + * Get the next nodes of a node within the same function + * + * @param node The node to get successors for + * @return Vector of successor nodes + */ + std::vector getNextNodes(const ICFGNode* node) const; + + /** + * Get the next nodes outside a cycle + * + * @param cycle The cycle to get exit successors for + * @return Vector of successor nodes outside the cycle + */ + std::vector getNextNodesOfCycle(const ICFGCycleWTO* cycle) const; + /** * handle SVF Statement like CmpStmt, CallStmt, GepStmt, LoadStmt, StoreStmt, etc. @@ -299,6 +330,7 @@ class AbstractInterpretation Map funcToWTO; Set> nonRecursiveCallSites; Set recursiveFuns; + Map cycleHeadToCycle; bool hasAbsStateFromTrace(const ICFGNode* node) diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index b3aed674f..c5ef275db 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -142,6 +142,27 @@ void AbstractInterpretation::initWTO() funcToWTO[it->second->getFunction()] = iwto; } } + + // Build cycleHeadToCycle map for all functions + // This maps cycle head nodes to their corresponding WTO cycles for efficient lookup + for (auto& [func, wto] : funcToWTO) + { + // Recursive lambda to collect cycle heads from nested WTO components + std::function&)> collectCycleHeads = + [&](const std::list& comps) + { + for (const ICFGWTOComp* comp : comps) + { + if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(comp)) + { + cycleHeadToCycle[cycle->head()->getICFGNode()] = cycle; + // Recursively collect nested cycle heads + collectCycleHeads(cycle->getWTOComponents()); + } + } + }; + collectCycleHeads(wto->getWTOComponents()); + } } /// Program entry @@ -154,8 +175,9 @@ void AbstractInterpretation::analyse() icfg->getGlobalICFGNode())[PAG::getPAG()->getBlkPtr()] = IntervalValue::top(); if (const CallGraphNode* cgn = svfir->getCallGraph()->getCallGraphNode("main")) { - const ICFGWTO* wto = funcToWTO[cgn->getFunction()]; - handleWTOComponents(wto->getWTOComponents()); + // Use worklist-based function handling instead of recursive WTO component handling + const ICFGNode* mainEntry = icfg->getFunEntryICFGNode(cgn->getFunction()); + handleFunction(mainEntry); } } @@ -576,6 +598,215 @@ void AbstractInterpretation::handleSingletonWTO(const ICFGSingletonWTO *icfgSing stat->countStateSize(); } +/** + * Handle an ICFG node by merging states from predecessors and processing statements + * Returns true if the abstract state has changed, false if fixpoint reached or infeasible + */ +bool AbstractInterpretation::handleICFGNode(const ICFGNode* node) +{ + // Store the previous state for fixpoint detection + AbstractState prevState; + bool hadPrevState = hasAbsStateFromTrace(node); + if (hadPrevState) + prevState = abstractTrace[node]; + + // For function entry nodes, initialize state from caller or global + bool isFunEntry = SVFUtil::isa(node); + if (isFunEntry) + { + // Try to merge from predecessors first (handles call edges) + if (!mergeStatesFromPredecessors(node)) + { + // No predecessors with state - initialize from caller or global + if (!callSiteStack.empty()) + { + // Get state from the most recent call site + const CallICFGNode* caller = callSiteStack.back(); + if (hasAbsStateFromTrace(caller)) + { + abstractTrace[node] = abstractTrace[caller]; + } + else + { + abstractTrace[node] = AbstractState(); + } + } + else + { + // This is the main function entry, inherit from global node + const ICFGNode* globalNode = icfg->getGlobalICFGNode(); + if (hasAbsStateFromTrace(globalNode)) + { + abstractTrace[node] = abstractTrace[globalNode]; + } + else + { + abstractTrace[node] = AbstractState(); + } + } + } + } + else + { + // Merge states from predecessors + if (!mergeStatesFromPredecessors(node)) + return false; + } + + stat->getBlockTrace()++; + stat->getICFGNodeTrace()++; + + // Handle SVF statements + for (const SVFStmt *stmt: node->getSVFStmts()) + { + handleSVFStatement(stmt); + } + + // Handle call sites + if (const CallICFGNode* callNode = SVFUtil::dyn_cast(node)) + { + handleCallSite(callNode); + } + + // Run detectors + for (auto& detector: detectors) + detector->detect(getAbsStateFromTrace(node), node); + stat->countStateSize(); + + // Check if state changed (for fixpoint detection) + // For entry nodes on first visit, always return true to process successors + if (isFunEntry && !hadPrevState) + return true; + + if (abstractTrace[node] == prevState) + return false; + + return true; +} + +/** + * Get the next nodes of a node within the same function + * For CallICFGNode, includes shortcut to RetICFGNode + */ +std::vector AbstractInterpretation::getNextNodes(const ICFGNode* node) const +{ + std::vector outEdges; + for (const ICFGEdge* edge : node->getOutEdges()) + { + const ICFGNode* dst = edge->getDstNode(); + // Only nodes inside the same function are included + if (dst->getFun() == node->getFun()) + { + outEdges.push_back(dst); + } + } + if (const CallICFGNode* callNode = SVFUtil::dyn_cast(node)) + { + // Shortcut to the RetICFGNode + const ICFGNode* retNode = callNode->getRetICFGNode(); + outEdges.push_back(retNode); + } + return outEdges; +} + +/** + * Get the next nodes outside a cycle + * Inner cycles are skipped since their next nodes cannot be outside the outer cycle + */ +std::vector AbstractInterpretation::getNextNodesOfCycle(const ICFGCycleWTO* cycle) const +{ + Set cycleNodes; + // Insert the head of the cycle and all component nodes + cycleNodes.insert(cycle->head()->getICFGNode()); + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + cycleNodes.insert(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + cycleNodes.insert(subCycle->head()->getICFGNode()); + } + } + + std::vector outEdges; + + // Check head's successors + std::vector nextNodes = getNextNodes(cycle->head()->getICFGNode()); + for (const ICFGNode* nextNode : nextNodes) + { + // Only nodes that point outside the cycle are included + if (cycleNodes.find(nextNode) == cycleNodes.end()) + { + outEdges.push_back(nextNode); + } + } + + // Check each component's successors + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + std::vector compNextNodes = getNextNodes(singleton->getICFGNode()); + for (const ICFGNode* nextNode : compNextNodes) + { + if (cycleNodes.find(nextNode) == cycleNodes.end()) + { + outEdges.push_back(nextNode); + } + } + } + // Skip inner cycles - they are handled within the outer cycle + } + return outEdges; +} + +/** + * Handle a function using worklist algorithm + * This replaces the recursive WTO component handling with explicit worklist iteration + */ +void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) +{ + FIFOWorkList worklist; + worklist.push(funEntry); + + while (!worklist.empty()) + { + const ICFGNode* node = worklist.pop(); + + // Check if this node is a cycle head + if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end()) + { + const ICFGCycleWTO* cycle = cycleHeadToCycle[node]; + handleCycleWTO(cycle); + + // Push nodes outside the cycle to the worklist + std::vector cycleNextNodes = getNextNodesOfCycle(cycle); + for (const ICFGNode* nextNode : cycleNextNodes) + { + worklist.push(nextNode); + } + } + else + { + // Handle regular node + if (!handleICFGNode(node)) + { + // Fixpoint reached or infeasible, skip successors + continue; + } + + // Push successor nodes to the worklist + std::vector nextNodes = getNextNodes(node); + for (const ICFGNode* nextNode : nextNodes) + { + worklist.push(nextNode); + } + } + } +} + /** * @brief Handle two types of WTO components (singleton and cycle) */ @@ -721,8 +952,9 @@ void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) callSiteStack.push_back(callNode); - const ICFGWTO* wto = funcToWTO[calleeFun]; - handleWTOComponents(wto->getWTOComponents()); + // Use worklist-based function handling instead of recursive WTO component handling + const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(calleeFun); + handleFunction(calleeEntry); callSiteStack.pop_back(); // handle Ret node @@ -762,8 +994,10 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) callSiteStack.push_back(callNode); abstractTrace[callNode] = as; - const ICFGWTO* wto = funcToWTO[callfun]; - handleWTOComponents(wto->getWTOComponents()); + // Use worklist-based function handling instead of recursive WTO component handling + const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callfun); + handleFunction(calleeEntry); + callSiteStack.pop_back(); // handle Ret node const RetICFGNode* retNode = callNode->getRetICFGNode(); @@ -771,26 +1005,32 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) } } -/// handle wto cycle (loop) -void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO*cycle) +/// handle wto cycle (loop) using worklist-compatible widening/narrowing iteration +void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); - // Flag to indicate if we are in the increasing phase + // Flag to indicate if we are in the increasing (widening) phase bool increasing = true; - // Infinite loop until a fixpoint is reached, + u32_t widen_delay = Options::WidenDelay(); + + // Infinite loop until a fixpoint is reached for (u32_t cur_iter = 0;; cur_iter++) { - // Start widening or narrowing if cur_iter >= widen threshold (widen delay) - if (cur_iter >= Options::WidenDelay()) + // Get the abstract state before processing the cycle head + AbstractState prev_head_state; + if (hasAbsStateFromTrace(cycle_head)) + prev_head_state = abstractTrace[cycle_head]; + + // Process the cycle head node + handleICFGNode(cycle_head); + AbstractState cur_head_state = abstractTrace[cycle_head]; + + // Start widening or narrowing if cur_iter >= widen delay threshold + if (cur_iter >= widen_delay) { - // Widen or narrow after processing cycle head node - AbstractState prev_head_state = abstractTrace[cycle_head]; - handleWTOComponent(cycle->head()); - AbstractState cur_head_state = abstractTrace[cycle_head]; if (increasing) { - - if (isRecursiveFun(cycle->head()->getICFGNode()->getFun()) && + if (isRecursiveFun(cycle_head->getFun()) && !(Options::HandleRecur() == WIDEN_ONLY || Options::HandleRecur() == WIDEN_NARROW)) { @@ -799,21 +1039,22 @@ void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO*cycle) assert(false && "Recursion mode TOP should not reach here!"); } - // Widening + // Apply widening operator abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state); if (abstractTrace[cycle_head] == prev_head_state) { + // Widening fixpoint reached, switch to narrowing phase increasing = false; continue; } } else { - // Narrowing, use different modes for nodes within recursions - if (isRecursiveFun(cycle->head()->getICFGNode()->getFun())) + // Narrowing phase - use different modes for nodes within recursions + if (isRecursiveFun(cycle_head->getFun())) { - // For nodes in recursions, skip narrowing in WIDEN_TOP mode + // For nodes in recursions, skip narrowing in WIDEN_ONLY mode if (Options::HandleRecur() == WIDEN_ONLY) { break; @@ -821,41 +1062,44 @@ void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO*cycle) // Perform normal narrowing in WIDEN_NARROW mode else if (Options::HandleRecur() == WIDEN_NARROW) { - // Widening's fixpoint reached in the widening phase, switch to narrowing abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); if (abstractTrace[cycle_head] == prev_head_state) { - // Narrowing's fixpoint reached in the narrowing phase, exit loop + // Narrowing fixpoint reached, exit loop break; } } - // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions, - // thus should not reach this branch else { assert(false && "Recursion mode TOP should not reach here"); } } - // For nodes outside recursions, perform normal narrowing else { - // Widening's fixpoint reached in the widening phase, switch to narrowing + // For nodes outside recursions, perform normal narrowing abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); if (abstractTrace[cycle_head] == prev_head_state) { - // Narrowing's fixpoint reached in the narrowing phase, exit loop + // Narrowing fixpoint reached, exit loop break; } } } } - else + + // Process cycle body components + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) { - // Handle the cycle head - handleWTOComponent(cycle->head()); + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + handleICFGNode(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + // Handle nested cycle recursively + handleCycleWTO(subCycle); + } } - // Handle the cycle body - handleWTOComponents(cycle->getWTOComponents()); } } From 353871db007a9bf8daf0b49efa480b901795c6ed Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Wed, 28 Jan 2026 15:26:00 +1100 Subject: [PATCH 02/18] sync with SSA Ass3 --- .../AE/Svfexe/AbstractInterpretation.h | 4 - svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 208 ++++++++---------- 2 files changed, 91 insertions(+), 121 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index e06322887..d5f594e68 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -220,10 +220,6 @@ class AbstractInterpretation */ virtual void handleCycleWTO(const ICFGCycleWTO* cycle); - void handleWTOComponents(const std::list& wtoComps); - - void handleWTOComponent(const ICFGWTOComp* wtoComp); - /** * Handle a function using worklist algorithm * diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index c5ef275db..bcd98abdb 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -810,30 +810,101 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) /** * @brief Handle two types of WTO components (singleton and cycle) */ -void AbstractInterpretation::handleWTOComponents(const std::list& wtoComps) +void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO* cycle) { - for (const ICFGWTOComp* wtoNode : wtoComps) - { - handleWTOComponent(wtoNode); - } -} + const ICFGNode* cycle_head = cycle->head()->getICFGNode(); + // Flag to indicate if we are in the increasing (widening) phase + bool increasing = true; + u32_t widen_delay = Options::WidenDelay(); -void AbstractInterpretation::handleWTOComponent(const ICFGWTOComp* wtoNode) -{ - if (const ICFGSingletonWTO* node = SVFUtil::dyn_cast(wtoNode)) - { - if (mergeStatesFromPredecessors(node->getICFGNode())) - handleSingletonWTO(node); - } - // Handle WTO cycles - else if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(wtoNode)) + // Infinite loop until a fixpoint is reached + for (u32_t cur_iter = 0;; cur_iter++) { - if (mergeStatesFromPredecessors(cycle->head()->getICFGNode())) - handleCycleWTO(cycle); + // Get the abstract state before processing the cycle head + AbstractState prev_head_state; + if (hasAbsStateFromTrace(cycle_head)) + prev_head_state = abstractTrace[cycle_head]; + + // Process the cycle head node + handleICFGNode(cycle_head); + AbstractState cur_head_state = abstractTrace[cycle_head]; + + // Start widening or narrowing if cur_iter >= widen delay threshold + if (cur_iter >= widen_delay) + { + if (increasing) + { + if (isRecursiveFun(cycle_head->getFun()) && + !(Options::HandleRecur() == WIDEN_ONLY || + Options::HandleRecur() == WIDEN_NARROW)) + { + // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions, + // thus should not reach this branch + assert(false && "Recursion mode TOP should not reach here!"); + } + + // Apply widening operator + abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state); + + if (abstractTrace[cycle_head] == prev_head_state) + { + // Widening fixpoint reached, switch to narrowing phase + increasing = false; + continue; + } + } + else + { + // Narrowing phase - use different modes for nodes within recursions + if (isRecursiveFun(cycle_head->getFun())) + { + // For nodes in recursions, skip narrowing in WIDEN_ONLY mode + if (Options::HandleRecur() == WIDEN_ONLY) + { + break; + } + // Perform normal narrowing in WIDEN_NARROW mode + else if (Options::HandleRecur() == WIDEN_NARROW) + { + abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); + if (abstractTrace[cycle_head] == prev_head_state) + { + // Narrowing fixpoint reached, exit loop + break; + } + } + else + { + assert(false && "Recursion mode TOP should not reach here"); + } + } + else + { + // For nodes outside recursions, perform normal narrowing + abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); + if (abstractTrace[cycle_head] == prev_head_state) + { + // Narrowing fixpoint reached, exit loop + break; + } + } + } + } + + // Process cycle body components + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + handleICFGNode(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + // Handle nested cycle recursively + handleCycleWTO(subCycle); + } + } } - // Assert false for unknown WTO types - else - assert(false && "unknown WTO type!"); } void AbstractInterpretation::handleCallSite(const ICFGNode* node) @@ -1006,103 +1077,6 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) } /// handle wto cycle (loop) using worklist-compatible widening/narrowing iteration -void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO* cycle) -{ - const ICFGNode* cycle_head = cycle->head()->getICFGNode(); - // Flag to indicate if we are in the increasing (widening) phase - bool increasing = true; - u32_t widen_delay = Options::WidenDelay(); - - // Infinite loop until a fixpoint is reached - for (u32_t cur_iter = 0;; cur_iter++) - { - // Get the abstract state before processing the cycle head - AbstractState prev_head_state; - if (hasAbsStateFromTrace(cycle_head)) - prev_head_state = abstractTrace[cycle_head]; - - // Process the cycle head node - handleICFGNode(cycle_head); - AbstractState cur_head_state = abstractTrace[cycle_head]; - - // Start widening or narrowing if cur_iter >= widen delay threshold - if (cur_iter >= widen_delay) - { - if (increasing) - { - if (isRecursiveFun(cycle_head->getFun()) && - !(Options::HandleRecur() == WIDEN_ONLY || - Options::HandleRecur() == WIDEN_NARROW)) - { - // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions, - // thus should not reach this branch - assert(false && "Recursion mode TOP should not reach here!"); - } - - // Apply widening operator - abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state); - - if (abstractTrace[cycle_head] == prev_head_state) - { - // Widening fixpoint reached, switch to narrowing phase - increasing = false; - continue; - } - } - else - { - // Narrowing phase - use different modes for nodes within recursions - if (isRecursiveFun(cycle_head->getFun())) - { - // For nodes in recursions, skip narrowing in WIDEN_ONLY mode - if (Options::HandleRecur() == WIDEN_ONLY) - { - break; - } - // Perform normal narrowing in WIDEN_NARROW mode - else if (Options::HandleRecur() == WIDEN_NARROW) - { - abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); - if (abstractTrace[cycle_head] == prev_head_state) - { - // Narrowing fixpoint reached, exit loop - break; - } - } - else - { - assert(false && "Recursion mode TOP should not reach here"); - } - } - else - { - // For nodes outside recursions, perform normal narrowing - abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); - if (abstractTrace[cycle_head] == prev_head_state) - { - // Narrowing fixpoint reached, exit loop - break; - } - } - } - } - - // Process cycle body components - for (const ICFGWTOComp* comp : cycle->getWTOComponents()) - { - if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) - { - handleICFGNode(singleton->getICFGNode()); - } - else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) - { - // Handle nested cycle recursively - handleCycleWTO(subCycle); - } - } - } -} - void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) { if (const AddrStmt *addr = SVFUtil::dyn_cast(stmt)) From 3847a44470e25879ed6d05b813cc8253eac6a332 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Thu, 29 Jan 2026 12:57:19 +1100 Subject: [PATCH 03/18] refactor recursion --- .../AE/Svfexe/AbstractInterpretation.h | 41 ++- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 322 +++++++++--------- 2 files changed, 208 insertions(+), 155 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index d5f594e68..79ee2c72a 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -218,7 +218,7 @@ class AbstractInterpretation * * @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles */ - virtual void handleCycleWTO(const ICFGCycleWTO* cycle); + virtual void handleICFGCycle(const ICFGCycleWTO* cycle); /** * Handle a function using worklist algorithm @@ -251,6 +251,13 @@ class AbstractInterpretation */ std::vector getNextNodesOfCycle(const ICFGCycleWTO* cycle) const; + /** + * Recursively collect cycle heads from nested WTO components + * + * @param comps The list of WTO components to collect cycle heads from + */ + void collectCycleHeads(const std::list& comps); + /** * handle SVF Statement like CmpStmt, CallStmt, GepStmt, LoadStmt, StoreStmt, etc. @@ -351,6 +358,38 @@ class AbstractInterpretation virtual bool isIndirectCall(const CallICFGNode* callNode); virtual void indirectCallFunPass(const CallICFGNode* callNode); + /// Recursion Handling Decision Methods + /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here + + /** + * Determines if a recursive call should be skipped (not inlined). + * - TOP mode: Always skip recursive calls + * - WIDEN_ONLY/WIDEN_NARROW: Skip only recursive callsites (calls within same SCC) + */ + bool shouldSkipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); + + /** + * Handles state for a skipped recursive call. + * Sets return value and stores to TOP (only meaningful in TOP mode). + */ + void handleSkippedRecursiveCall(const CallICFGNode* callNode); + + /** + * Determines if narrowing should be applied for a cycle head in a recursive function. + * - TOP mode: Should not reach here (asserts) + * - WIDEN_ONLY: Returns false (skip narrowing) + * - WIDEN_NARROW: Returns true (apply narrowing) + * For non-recursive functions, always returns true. + */ + bool shouldApplyNarrowingInRecursion(const FunObjVar* fun); + + /** + * Determines if a return edge should contribute to state merging. + * - TOP mode: Always include + * - WIDEN_ONLY/WIDEN_NARROW: Only if callsite has state + */ + bool shouldIncludeReturnEdge(const RetICFGNode* returnSite); + // there data should be shared with subclasses Map> func_map; diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index bcd98abdb..aac1765c5 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -73,14 +73,25 @@ AbstractInterpretation::~AbstractInterpretation() } /** - * @brief Compute WTO for each function partition entry + * @brief Recursively collect cycle heads from nested WTO components * - * This function first identifies function partition entries (pair: ), - * and then compute the IWTO for each pair. - * It does this by detecting call graph's strongly connected components (SCC). - * Each SCC forms a function partition, and any function that is invoked from outside its SCC - * is identified as an entry of the function partition. + * This helper function traverses the WTO component tree and builds the cycleHeadToCycle + * map, which maps each cycle head node to its corresponding ICFGCycleWTO object. + * This enables efficient O(1) lookup of cycles during analysis. */ +void AbstractInterpretation::collectCycleHeads(const std::list& comps) +{ + for (const ICFGWTOComp* comp : comps) + { + if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(comp)) + { + cycleHeadToCycle[cycle->head()->getICFGNode()] = cycle; + // Recursively collect nested cycle heads + collectCycleHeads(cycle->getWTOComponents()); + } + } +} + void AbstractInterpretation::initWTO() { AndersenWaveDiff* ander = AndersenWaveDiff::createAndersenWaveDiff(svfir); @@ -147,20 +158,6 @@ void AbstractInterpretation::initWTO() // This maps cycle head nodes to their corresponding WTO cycles for efficient lookup for (auto& [func, wto] : funcToWTO) { - // Recursive lambda to collect cycle heads from nested WTO components - std::function&)> collectCycleHeads = - [&](const std::list& comps) - { - for (const ICFGWTOComp* comp : comps) - { - if (const ICFGCycleWTO* cycle = SVFUtil::dyn_cast(comp)) - { - cycleHeadToCycle[cycle->head()->getICFGNode()] = cycle; - // Recursively collect nested cycle heads - collectCycleHeads(cycle->getWTOComponents()); - } - } - }; collectCycleHeads(wto->getWTOComponents()); } } @@ -237,21 +234,10 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode * icfgNo else if (const RetCFGEdge *retCfgEdge = SVFUtil::dyn_cast(edge)) { - switch (Options::HandleRecur()) - { - case TOP: + const RetICFGNode* returnSite = SVFUtil::dyn_cast(icfgNode); + if (shouldIncludeReturnEdge(returnSite)) { workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]); - break; - } - case WIDEN_ONLY: - case WIDEN_NARROW: - { - const RetICFGNode* returnSite = SVFUtil::dyn_cast(icfgNode); - const CallICFGNode* callSite = returnSite->getCallICFGNode(); - if (hasAbsStateFromTrace(callSite)) - workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]); - } } } else @@ -779,7 +765,7 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end()) { const ICFGCycleWTO* cycle = cycleHeadToCycle[node]; - handleCycleWTO(cycle); + handleICFGCycle(cycle); // Push nodes outside the cycle to the worklist std::vector cycleNextNodes = getNextNodesOfCycle(cycle); @@ -807,105 +793,6 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) } } -/** - * @brief Handle two types of WTO components (singleton and cycle) - */ -void AbstractInterpretation::handleCycleWTO(const ICFGCycleWTO* cycle) -{ - const ICFGNode* cycle_head = cycle->head()->getICFGNode(); - // Flag to indicate if we are in the increasing (widening) phase - bool increasing = true; - u32_t widen_delay = Options::WidenDelay(); - - // Infinite loop until a fixpoint is reached - for (u32_t cur_iter = 0;; cur_iter++) - { - // Get the abstract state before processing the cycle head - AbstractState prev_head_state; - if (hasAbsStateFromTrace(cycle_head)) - prev_head_state = abstractTrace[cycle_head]; - - // Process the cycle head node - handleICFGNode(cycle_head); - AbstractState cur_head_state = abstractTrace[cycle_head]; - - // Start widening or narrowing if cur_iter >= widen delay threshold - if (cur_iter >= widen_delay) - { - if (increasing) - { - if (isRecursiveFun(cycle_head->getFun()) && - !(Options::HandleRecur() == WIDEN_ONLY || - Options::HandleRecur() == WIDEN_NARROW)) - { - // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions, - // thus should not reach this branch - assert(false && "Recursion mode TOP should not reach here!"); - } - - // Apply widening operator - abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state); - - if (abstractTrace[cycle_head] == prev_head_state) - { - // Widening fixpoint reached, switch to narrowing phase - increasing = false; - continue; - } - } - else - { - // Narrowing phase - use different modes for nodes within recursions - if (isRecursiveFun(cycle_head->getFun())) - { - // For nodes in recursions, skip narrowing in WIDEN_ONLY mode - if (Options::HandleRecur() == WIDEN_ONLY) - { - break; - } - // Perform normal narrowing in WIDEN_NARROW mode - else if (Options::HandleRecur() == WIDEN_NARROW) - { - abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); - if (abstractTrace[cycle_head] == prev_head_state) - { - // Narrowing fixpoint reached, exit loop - break; - } - } - else - { - assert(false && "Recursion mode TOP should not reach here"); - } - } - else - { - // For nodes outside recursions, perform normal narrowing - abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); - if (abstractTrace[cycle_head] == prev_head_state) - { - // Narrowing fixpoint reached, exit loop - break; - } - } - } - } - - // Process cycle body components - for (const ICFGWTOComp* comp : cycle->getWTOComponents()) - { - if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) - { - handleICFGNode(singleton->getICFGNode()); - } - else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) - { - // Handle nested cycle recursively - handleCycleWTO(subCycle); - } - } - } -} void AbstractInterpretation::handleCallSite(const ICFGNode* node) { @@ -915,10 +802,6 @@ void AbstractInterpretation::handleCallSite(const ICFGNode* node) { extCallPass(callNode); } - else if (isRecursiveCall(callNode) && Options::HandleRecur() == TOP) - { - recursiveCallPass(callNode); - } else if (isDirectCall(callNode)) { directCallFunPass(callNode); @@ -990,6 +873,70 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, nonRecursiveCallSites.end(); } +/// Recursion Handling Decision Methods +/// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here + +bool AbstractInterpretation::shouldSkipRecursiveCall( + const CallICFGNode* callNode, const FunObjVar* callee) +{ + if (!isRecursiveFun(callee)) + return false; + + switch (Options::HandleRecur()) + { + case TOP: + return true; // Always skip in TOP mode + case WIDEN_ONLY: + case WIDEN_NARROW: + return isRecursiveCallSite(callNode, callee); // Skip only within-SCC calls + default: + assert(false && "Unknown recursion handling mode"); + return false; + } +} + +void AbstractInterpretation::handleSkippedRecursiveCall(const CallICFGNode* callNode) +{ + // Delegate to recursiveCallPass which sets return value and stores to TOP + recursiveCallPass(callNode); +} + +bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fun) +{ + if (!isRecursiveFun(fun)) + return true; // Non-recursive functions always apply narrowing + + switch (Options::HandleRecur()) + { + case TOP: + assert(false && "TOP mode should not reach cycle narrowing phase"); + return false; + case WIDEN_ONLY: + return false; // Skip narrowing for recursive functions + case WIDEN_NARROW: + return true; // Apply narrowing for recursive functions + default: + assert(false && "Unknown recursion handling mode"); + return false; + } +} + +bool AbstractInterpretation::shouldIncludeReturnEdge(const RetICFGNode* returnSite) +{ + switch (Options::HandleRecur()) + { + case TOP: + return true; // Always include in TOP mode + case WIDEN_ONLY: + case WIDEN_NARROW: + // Only include if the corresponding callsite has state + return hasAbsStateFromTrace(returnSite->getCallICFGNode()); + default: + assert(false && "Unknown recursion handling mode"); + return true; + } +} + bool AbstractInterpretation::isDirectCall(const CallICFGNode *callNode) { const FunObjVar *callfun =callNode->getCalledFunction(); @@ -1004,21 +951,16 @@ void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) abstractTrace[callNode] = as; - const FunObjVar *calleeFun =callNode->getCalledFunction(); - if (Options::HandleRecur() == WIDEN_ONLY || Options::HandleRecur() == WIDEN_NARROW) - { - // If this CallICFGNode is a recursive callsite (i.e. this Node - // resides in a recursive function 'fun' and its callee function is - // in the same SCC with the fun), then skip it. Since the callee - // function is handled during the handling of WTO of the whole recursion. - if (isRecursiveCallSite(callNode, calleeFun)) - return; - } - else + const FunObjVar *calleeFun = callNode->getCalledFunction(); + + // Check if this recursive call should be skipped + if (shouldSkipRecursiveCall(callNode, calleeFun)) { - // When Options::HandleRecur() == TOP, skipRecursiveCall will handle recursions, - // thus should not reach this branch - assert(false && "Recursion mode TOP should not reach here!"); + // In TOP mode, set return value and stores to TOP + // In WIDEN_ONLY/WIDEN_NARROW, just skip (WTO handles it) + if (Options::HandleRecur() == TOP) + handleSkippedRecursiveCall(callNode); + return; } callSiteStack.push_back(callNode); @@ -1055,10 +997,14 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) if(const FunObjVar* funObjVar = SVFUtil::dyn_cast(func_var)) { - if (Options::HandleRecur() == WIDEN_ONLY || Options::HandleRecur() == WIDEN_NARROW) + // Check if this recursive call should be skipped + if (shouldSkipRecursiveCall(callNode, funObjVar)) { - if (isRecursiveCallSite(callNode, funObjVar)) - return; + // In TOP mode, set return value and stores to TOP + // In WIDEN_ONLY/WIDEN_NARROW, just skip (WTO handles it) + if (Options::HandleRecur() == TOP) + handleSkippedRecursiveCall(callNode); + return; } const FunObjVar* callfun = funObjVar->getFunction(); @@ -1077,6 +1023,74 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) } /// handle wto cycle (loop) using worklist-compatible widening/narrowing iteration +void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) +{ + const ICFGNode* cycle_head = cycle->head()->getICFGNode(); + // Flag to indicate if we are in the increasing (widening) phase + bool increasing = true; + u32_t widen_delay = Options::WidenDelay(); + + // Infinite loop until a fixpoint is reached + for (u32_t cur_iter = 0;; cur_iter++) + { + // Get the abstract state before processing the cycle head + AbstractState prev_head_state; + if (hasAbsStateFromTrace(cycle_head)) + prev_head_state = abstractTrace[cycle_head]; + + // Process the cycle head node + handleICFGNode(cycle_head); + AbstractState cur_head_state = abstractTrace[cycle_head]; + + // Start widening or narrowing if cur_iter >= widen delay threshold + if (cur_iter >= widen_delay) + { + if (increasing) + { + // Apply widening operator + abstractTrace[cycle_head] = prev_head_state.widening(cur_head_state); + + if (abstractTrace[cycle_head] == prev_head_state) + { + // Widening fixpoint reached, switch to narrowing phase + increasing = false; + continue; + } + } + else + { + // Narrowing phase - check if narrowing should be applied + if (!shouldApplyNarrowingInRecursion(cycle_head->getFun())) + { + break; + } + + // Apply narrowing + abstractTrace[cycle_head] = prev_head_state.narrowing(cur_head_state); + if (abstractTrace[cycle_head] == prev_head_state) + { + // Narrowing fixpoint reached, exit loop + break; + } + } + } + + // Process cycle body components + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + handleICFGNode(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + // Handle nested cycle recursively + handleICFGCycle(subCycle); + } + } + } +} + void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) { if (const AddrStmt *addr = SVFUtil::dyn_cast(stmt)) From b7be147f13a77f5cfc22b04abdeea98e0ff58373 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Sat, 31 Jan 2026 22:42:34 +1100 Subject: [PATCH 04/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 15 +++----- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 35 ++++++------------- 2 files changed, 15 insertions(+), 35 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 79ee2c72a..e0b3dbd8a 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -362,17 +362,12 @@ class AbstractInterpretation /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here /** - * Determines if a recursive call should be skipped (not inlined). - * - TOP mode: Always skip recursive calls - * - WIDEN_ONLY/WIDEN_NARROW: Skip only recursive callsites (calls within same SCC) + * Attempts to handle a recursive call. Returns true if the call was handled + * (caller should return early), false if normal processing should continue. + * - TOP mode: Always handles recursive calls (sets return/stores to TOP) + * - WIDEN_ONLY/WIDEN_NARROW: Only handles recursive callsites within same SCC */ - bool shouldSkipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); - - /** - * Handles state for a skipped recursive call. - * Sets return value and stores to TOP (only meaningful in TOP mode). - */ - void handleSkippedRecursiveCall(const CallICFGNode* callNode); + bool handleRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); /** * Determines if narrowing should be applied for a cycle head in a recursive function. diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index aac1765c5..cb3851720 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -876,7 +876,7 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, /// Recursion Handling Decision Methods /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here -bool AbstractInterpretation::shouldSkipRecursiveCall( +bool AbstractInterpretation::handleRecursiveCall( const CallICFGNode* callNode, const FunObjVar* callee) { if (!isRecursiveFun(callee)) @@ -885,22 +885,19 @@ bool AbstractInterpretation::shouldSkipRecursiveCall( switch (Options::HandleRecur()) { case TOP: - return true; // Always skip in TOP mode + // Always skip recursive calls, set return value and stores to TOP + recursiveCallPass(callNode); + return true; case WIDEN_ONLY: case WIDEN_NARROW: - return isRecursiveCallSite(callNode, callee); // Skip only within-SCC calls + // Only skip recursive callsites within same SCC (WTO handles the analysis) + return isRecursiveCallSite(callNode, callee); default: assert(false && "Unknown recursion handling mode"); return false; } } -void AbstractInterpretation::handleSkippedRecursiveCall(const CallICFGNode* callNode) -{ - // Delegate to recursiveCallPass which sets return value and stores to TOP - recursiveCallPass(callNode); -} - bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fun) { if (!isRecursiveFun(fun)) @@ -953,15 +950,9 @@ void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) const FunObjVar *calleeFun = callNode->getCalledFunction(); - // Check if this recursive call should be skipped - if (shouldSkipRecursiveCall(callNode, calleeFun)) - { - // In TOP mode, set return value and stores to TOP - // In WIDEN_ONLY/WIDEN_NARROW, just skip (WTO handles it) - if (Options::HandleRecur() == TOP) - handleSkippedRecursiveCall(callNode); + // Handle recursive call if applicable (returns true if handled) + if (handleRecursiveCall(callNode, calleeFun)) return; - } callSiteStack.push_back(callNode); @@ -997,15 +988,9 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) if(const FunObjVar* funObjVar = SVFUtil::dyn_cast(func_var)) { - // Check if this recursive call should be skipped - if (shouldSkipRecursiveCall(callNode, funObjVar)) - { - // In TOP mode, set return value and stores to TOP - // In WIDEN_ONLY/WIDEN_NARROW, just skip (WTO handles it) - if (Options::HandleRecur() == TOP) - handleSkippedRecursiveCall(callNode); + // Handle recursive call if applicable (returns true if handled) + if (handleRecursiveCall(callNode, funObjVar)) return; - } const FunObjVar* callfun = funObjVar->getFunction(); callSiteStack.push_back(callNode); From e7f18f80487e39e161bd2762431f63b86cc8cccc Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Sat, 31 Jan 2026 23:04:07 +1100 Subject: [PATCH 05/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 7 ------- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 21 ++++--------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index e0b3dbd8a..952744eb8 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -378,13 +378,6 @@ class AbstractInterpretation */ bool shouldApplyNarrowingInRecursion(const FunObjVar* fun); - /** - * Determines if a return edge should contribute to state merging. - * - TOP mode: Always include - * - WIDEN_ONLY/WIDEN_NARROW: Only if callsite has state - */ - bool shouldIncludeReturnEdge(const RetICFGNode* returnSite); - // there data should be shared with subclasses Map> func_map; diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index cb3851720..6df08a8df 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -234,8 +234,10 @@ bool AbstractInterpretation::mergeStatesFromPredecessors(const ICFGNode * icfgNo else if (const RetCFGEdge *retCfgEdge = SVFUtil::dyn_cast(edge)) { - const RetICFGNode* returnSite = SVFUtil::dyn_cast(icfgNode); - if (shouldIncludeReturnEdge(returnSite)) + // Only include return edge if the corresponding callsite was processed + // (skipped recursive callsites in WIDEN_ONLY/WIDEN_NARROW won't have state) + const RetICFGNode* retNode = SVFUtil::dyn_cast(icfgNode); + if (hasAbsStateFromTrace(retNode->getCallICFGNode())) { workList.push_back(abstractTrace[retCfgEdge->getSrcNode()]); } @@ -918,21 +920,6 @@ bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fu } } -bool AbstractInterpretation::shouldIncludeReturnEdge(const RetICFGNode* returnSite) -{ - switch (Options::HandleRecur()) - { - case TOP: - return true; // Always include in TOP mode - case WIDEN_ONLY: - case WIDEN_NARROW: - // Only include if the corresponding callsite has state - return hasAbsStateFromTrace(returnSite->getCallICFGNode()); - default: - assert(false && "Unknown recursion handling mode"); - return true; - } -} bool AbstractInterpretation::isDirectCall(const CallICFGNode *callNode) { From 500c22f9ad0ea7e201ca40810551fe00750f1af2 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 12:05:22 +1100 Subject: [PATCH 06/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 29 +++++++++++++++---- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 17 ++++++----- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 952744eb8..1a94378c2 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -362,12 +362,31 @@ class AbstractInterpretation /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here /** - * Attempts to handle a recursive call. Returns true if the call was handled - * (caller should return early), false if normal processing should continue. - * - TOP mode: Always handles recursive calls (sets return/stores to TOP) - * - WIDEN_ONLY/WIDEN_NARROW: Only handles recursive callsites within same SCC + * Determines whether to skip (not inline) a call to a potentially recursive function. + * Returns true if the call should be skipped, false if normal inlining should proceed. + * + * Example: + * int factorial(int n) { + * if (n <= 1) return 1; + * return n * factorial(n - 1); // inner call (recursive callsite) + * } + * int main() { + * return factorial(5); // outer call (entry into recursion) + * } + * + * Behavior by mode: + * - TOP mode: + * Both calls are skipped. Return value and stores are set to TOP. + * Result: factorial returns [-inf, +inf] + * + * - WIDEN_ONLY / WIDEN_NARROW mode: + * Outer call (main -> factorial): NOT skipped, inlined normally. + * Inner call (factorial -> factorial): Skipped, WTO cycle handles the iteration. + * The recursive function body is analyzed via handleICFGCycle() with widening + * (and narrowing for WIDEN_NARROW) to reach a fixpoint. + * Result: factorial returns [10000, +inf] (WIDEN_ONLY) or [10000, 10000] (WIDEN_NARROW) */ - bool handleRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); + bool skipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); /** * Determines if narrowing should be applied for a cycle head in a recursive function. diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 6df08a8df..6a10c6f82 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -878,21 +878,24 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, /// Recursion Handling Decision Methods /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here -bool AbstractInterpretation::handleRecursiveCall( +bool AbstractInterpretation::skipRecursiveCall( const CallICFGNode* callNode, const FunObjVar* callee) { + // Non-recursive function: never skip, always inline if (!isRecursiveFun(callee)) return false; switch (Options::HandleRecur()) { case TOP: - // Always skip recursive calls, set return value and stores to TOP + // Skip all calls to recursive functions, set return value and stores to TOP recursiveCallPass(callNode); return true; case WIDEN_ONLY: case WIDEN_NARROW: - // Only skip recursive callsites within same SCC (WTO handles the analysis) + // Skip only recursive callsites (within same SCC), not the entry call. + // Entry call is inlined; recursive callsites are skipped so that + // handleICFGCycle() can analyze the function body with widening/narrowing. return isRecursiveCallSite(callNode, callee); default: assert(false && "Unknown recursion handling mode"); @@ -937,8 +940,8 @@ void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) const FunObjVar *calleeFun = callNode->getCalledFunction(); - // Handle recursive call if applicable (returns true if handled) - if (handleRecursiveCall(callNode, calleeFun)) + // Skip recursive call if applicable (returns true if skipped) + if (skipRecursiveCall(callNode, calleeFun)) return; callSiteStack.push_back(callNode); @@ -975,8 +978,8 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) if(const FunObjVar* funObjVar = SVFUtil::dyn_cast(func_var)) { - // Handle recursive call if applicable (returns true if handled) - if (handleRecursiveCall(callNode, funObjVar)) + // Skip recursive call if applicable (returns true if skipped) + if (skipRecursiveCall(callNode, funObjVar)) return; const FunObjVar* callfun = funObjVar->getFunction(); From 94ab144b92ab0a5c07fbc78b2175add5434de19f Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 16:25:29 +1100 Subject: [PATCH 07/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 24 +++--- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 79 +++++++++++++------ 2 files changed, 66 insertions(+), 37 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 1a94378c2..ec83155bd 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -358,13 +358,14 @@ class AbstractInterpretation virtual bool isIndirectCall(const CallICFGNode* callNode); virtual void indirectCallFunPass(const CallICFGNode* callNode); - /// Recursion Handling Decision Methods - /// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here - /** * Determines whether to skip (not inline) a call to a potentially recursive function. * Returns true if the call should be skipped, false if normal inlining should proceed. * + * This function is mode-independent: it only checks whether the call is a recursive + * callsite (within the same SCC). The actual handling of recursive functions is done + * uniformly via handleICFGCycle(), with mode-specific behavior inside that function. + * * Example: * int factorial(int n) { * if (n <= 1) return 1; @@ -374,17 +375,14 @@ class AbstractInterpretation * return factorial(5); // outer call (entry into recursion) * } * - * Behavior by mode: - * - TOP mode: - * Both calls are skipped. Return value and stores are set to TOP. - * Result: factorial returns [-inf, +inf] + * For all modes (TOP/WIDEN_ONLY/WIDEN_NARROW): + * - Outer call (main -> factorial): NOT skipped, inlined into handleICFGCycle() + * - Inner call (factorial -> factorial): Skipped (back-edge of the cycle) * - * - WIDEN_ONLY / WIDEN_NARROW mode: - * Outer call (main -> factorial): NOT skipped, inlined normally. - * Inner call (factorial -> factorial): Skipped, WTO cycle handles the iteration. - * The recursive function body is analyzed via handleICFGCycle() with widening - * (and narrowing for WIDEN_NARROW) to reach a fixpoint. - * Result: factorial returns [10000, +inf] (WIDEN_ONLY) or [10000, 10000] (WIDEN_NARROW) + * The difference between modes is in handleICFGCycle(): + * - TOP: Sets all values to TOP without iteration. Result: [-inf, +inf] + * - WIDEN_ONLY: Widening iteration only. Result: [10000, +inf] + * - WIDEN_NARROW: Widening + narrowing. Result: [10000, 10000] */ bool skipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 6a10c6f82..3b4841397 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -875,9 +875,6 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, nonRecursiveCallSites.end(); } -/// Recursion Handling Decision Methods -/// All recursion mode (TOP/WIDEN_ONLY/WIDEN_NARROW) logic is centralized here - bool AbstractInterpretation::skipRecursiveCall( const CallICFGNode* callNode, const FunObjVar* callee) { @@ -885,33 +882,26 @@ bool AbstractInterpretation::skipRecursiveCall( if (!isRecursiveFun(callee)) return false; - switch (Options::HandleRecur()) - { - case TOP: - // Skip all calls to recursive functions, set return value and stores to TOP - recursiveCallPass(callNode); - return true; - case WIDEN_ONLY: - case WIDEN_NARROW: - // Skip only recursive callsites (within same SCC), not the entry call. - // Entry call is inlined; recursive callsites are skipped so that - // handleICFGCycle() can analyze the function body with widening/narrowing. - return isRecursiveCallSite(callNode, callee); - default: - assert(false && "Unknown recursion handling mode"); - return false; - } + // For recursive functions, skip only recursive callsites (within same SCC). + // Entry calls (from outside SCC) are not skipped - they are inlined so that + // handleICFGCycle() can analyze the function body. + // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW). + return isRecursiveCallSite(callNode, callee); } bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fun) { + // Non-recursive functions (regular loops): always apply narrowing if (!isRecursiveFun(fun)) - return true; // Non-recursive functions always apply narrowing + return true; + // Recursive functions: depends on mode + // Note: TOP mode handles recursive cycles separately in handleICFGCycle, + // so this should not be reached for recursive functions in TOP mode. switch (Options::HandleRecur()) { case TOP: - assert(false && "TOP mode should not reach cycle narrowing phase"); + assert(false && "TOP mode should not reach cycle narrowing phase for recursive functions"); return false; case WIDEN_ONLY: return false; // Skip narrowing for recursive functions @@ -997,15 +987,56 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) } } -/// handle wto cycle (loop) using worklist-compatible widening/narrowing iteration +/// handle wto cycle (loop) using widening/narrowing iteration +/// +/// This handles both regular loops and recursive function cycles. +/// The behavior depends on Options::HandleRecur() for recursive functions: +/// +/// - TOP mode (recursive function cycle only): +/// Sets all values to TOP without iteration. This is the most conservative +/// but fastest approach. +/// +/// - WIDEN_ONLY mode: +/// Iterates with widening until fixpoint. No narrowing phase. +/// For recursive functions, this gives upper bounds (e.g., [10000, +inf]). +/// +/// - WIDEN_NARROW mode: +/// Iterates with widening, then narrows to refine the result. +/// For recursive functions, this gives precise bounds (e.g., [10000, 10000]). +/// +/// For regular (non-recursive) loops, all modes use widening + narrowing. void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); - // Flag to indicate if we are in the increasing (widening) phase + + // TOP mode for recursive function cycles: set states to TOP and return immediately + if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun())) + { + // Process cycle head once to establish state + handleICFGNode(cycle_head); + + // Process cycle body once, setting all states + for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + { + if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) + { + handleICFGNode(singleton->getICFGNode()); + } + else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) + { + handleICFGCycle(subCycle); + } + } + + // Set cycle head state to TOP to represent unknown recursive result + abstractTrace[cycle_head] = abstractTrace[cycle_head].top(); + return; + } + + // WIDEN_ONLY / WIDEN_NARROW modes (and regular loops): iterate until fixpoint bool increasing = true; u32_t widen_delay = Options::WidenDelay(); - // Infinite loop until a fixpoint is reached for (u32_t cur_iter = 0;; cur_iter++) { // Get the abstract state before processing the cycle head From 809a397af306e947e4780e965449bea36ab0f2e0 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 21:17:12 +1100 Subject: [PATCH 08/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- svf/include/AE/Svfexe/AbstractInterpretation.h | 15 +++++++++------ svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 6 +++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index ec83155bd..672f7922f 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -387,13 +387,16 @@ class AbstractInterpretation bool skipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); /** - * Determines if narrowing should be applied for a cycle head in a recursive function. - * - TOP mode: Should not reach here (asserts) - * - WIDEN_ONLY: Returns false (skip narrowing) - * - WIDEN_NARROW: Returns true (apply narrowing) - * For non-recursive functions, always returns true. + * Determines if narrowing should be applied for a cycle head. + * Called during the narrowing phase of handleICFGCycle(). + * + * For non-recursive functions (regular loops): always returns true. + * For recursive functions: depends on Options::HandleRecur(): + * - TOP mode: Should not reach here (asserts), TOP mode exits early in handleICFGCycle + * - WIDEN_ONLY: Returns false (skip narrowing) + * - WIDEN_NARROW: Returns true (apply narrowing) */ - bool shouldApplyNarrowingInRecursion(const FunObjVar* fun); + bool shouldApplyNarrowing(const FunObjVar* fun); // there data should be shared with subclasses Map> func_map; diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 3b4841397..8aec1c422 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -889,7 +889,7 @@ bool AbstractInterpretation::skipRecursiveCall( return isRecursiveCallSite(callNode, callee); } -bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fun) +bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) { // Non-recursive functions (regular loops): always apply narrowing if (!isRecursiveFun(fun)) @@ -901,7 +901,7 @@ bool AbstractInterpretation::shouldApplyNarrowingInRecursion(const FunObjVar* fu switch (Options::HandleRecur()) { case TOP: - assert(false && "TOP mode should not reach cycle narrowing phase for recursive functions"); + assert(false && "TOP mode should not reach narrowing phase for recursive functions"); return false; case WIDEN_ONLY: return false; // Skip narrowing for recursive functions @@ -1066,7 +1066,7 @@ void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) else { // Narrowing phase - check if narrowing should be applied - if (!shouldApplyNarrowingInRecursion(cycle_head->getFun())) + if (!shouldApplyNarrowing(cycle_head->getFun())) { break; } From ec7f7ca515d156dd7181fb346704c062943dca1b Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 21:37:19 +1100 Subject: [PATCH 09/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- svf/include/AE/Svfexe/AbstractInterpretation.h | 5 +++-- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 672f7922f..4fcfbefe9 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -267,11 +267,12 @@ class AbstractInterpretation virtual void handleSVFStatement(const SVFStmt* stmt); /** - * Check if this callnode is recursive call and skip it. + * Sets all store values in the recursive function to TOP. + * This is called when skipping a recursive call in TOP mode. * * @param callnode CallICFGNode which calls a recursive function */ - virtual void SkipRecursiveCall(const CallICFGNode* callnode); + virtual void setRecursiveCallStoresToTop(const CallICFGNode* callnode); /** diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 8aec1c422..7eb987661 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -852,7 +852,7 @@ bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode) void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); - SkipRecursiveCall(callNode); + setRecursiveCallStoresToTop(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); if (retNode->getSVFStmts().size() > 0) { @@ -1158,7 +1158,7 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) !getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr()); } -void AbstractInterpretation::SkipRecursiveCall(const CallICFGNode *callNode) +void AbstractInterpretation::setRecursiveCallStoresToTop(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); From 8b0605c46f99b7d5712fde1b39957cd33cb77115 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 21:47:57 +1100 Subject: [PATCH 10/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 10 ++- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 80 ++++++++++++------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 4fcfbefe9..6aa365152 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -385,7 +385,15 @@ class AbstractInterpretation * - WIDEN_ONLY: Widening iteration only. Result: [10000, +inf] * - WIDEN_NARROW: Widening + narrowing. Result: [10000, 10000] */ - bool skipRecursiveCall(const CallICFGNode* callNode, const FunObjVar* callee); + bool skipRecursiveCall(const CallICFGNode* callNode); + + /** + * Gets the callee function for a call node. + * For direct calls, returns callNode->getCalledFunction(). + * For indirect calls, resolves the callee through pointer analysis. + * Returns nullptr if callee cannot be determined. + */ + const FunObjVar* getCallee(const CallICFGNode* callNode); /** * Determines if narrowing should be applied for a cycle head. diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 7eb987661..a654be27d 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -875,9 +875,41 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, nonRecursiveCallSites.end(); } -bool AbstractInterpretation::skipRecursiveCall( - const CallICFGNode* callNode, const FunObjVar* callee) +const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode) { + // Direct call: get callee directly from call node + if (const FunObjVar* callee = callNode->getCalledFunction()) + return callee; + + // Indirect call: resolve callee through pointer analysis + const auto callsiteMaps = svfir->getIndirectCallsites(); + auto it = callsiteMaps.find(callNode); + if (it == callsiteMaps.end()) + return nullptr; + + NodeID call_id = it->second; + if (!hasAbsStateFromTrace(callNode)) + return nullptr; + + AbstractState& as = getAbsStateFromTrace(callNode); + if (!as.inVarToAddrsTable(call_id)) + return nullptr; + + AbstractValue Addrs = as[call_id]; + if (Addrs.getAddrs().empty()) + return nullptr; + + NodeID addr = *Addrs.getAddrs().begin(); + SVFVar* func_var = svfir->getGNode(as.getIDFromAddr(addr)); + return SVFUtil::dyn_cast(func_var); +} + +bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode) +{ + const FunObjVar* callee = getCallee(callNode); + if (!callee) + return false; + // Non-recursive function: never skip, always inline if (!isRecursiveFun(callee)) return false; @@ -928,12 +960,11 @@ void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) abstractTrace[callNode] = as; - const FunObjVar *calleeFun = callNode->getCalledFunction(); - // Skip recursive call if applicable (returns true if skipped) - if (skipRecursiveCall(callNode, calleeFun)) + if (skipRecursiveCall(callNode)) return; + const FunObjVar *calleeFun = callNode->getCalledFunction(); callSiteStack.push_back(callNode); // Use worklist-based function handling instead of recursive WTO component handling @@ -956,35 +987,26 @@ bool AbstractInterpretation::isIndirectCall(const CallICFGNode *callNode) void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); - const auto callsiteMaps = svfir->getIndirectCallsites(); - NodeID call_id = callsiteMaps.at(callNode); - if (!as.inVarToAddrsTable(call_id)) - { + + // Skip recursive call if applicable (returns true if skipped) + if (skipRecursiveCall(callNode)) return; - } - AbstractValue Addrs = as[call_id]; - NodeID addr = *Addrs.getAddrs().begin(); - SVFVar *func_var = svfir->getGNode(as.getIDFromAddr(addr)); - if(const FunObjVar* funObjVar = SVFUtil::dyn_cast(func_var)) - { - // Skip recursive call if applicable (returns true if skipped) - if (skipRecursiveCall(callNode, funObjVar)) - return; + const FunObjVar* callee = getCallee(callNode); + if (!callee) + return; - const FunObjVar* callfun = funObjVar->getFunction(); - callSiteStack.push_back(callNode); - abstractTrace[callNode] = as; + callSiteStack.push_back(callNode); + abstractTrace[callNode] = as; - // Use worklist-based function handling instead of recursive WTO component handling - const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callfun); - handleFunction(calleeEntry); + // Use worklist-based function handling instead of recursive WTO component handling + const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee); + handleFunction(calleeEntry); - callSiteStack.pop_back(); - // handle Ret node - const RetICFGNode* retNode = callNode->getRetICFGNode(); - abstractTrace[retNode] = abstractTrace[callNode]; - } + callSiteStack.pop_back(); + // handle Ret node + const RetICFGNode* retNode = callNode->getRetICFGNode(); + abstractTrace[retNode] = abstractTrace[callNode]; } /// handle wto cycle (loop) using widening/narrowing iteration From e6942d51c5762f827da99eb6e3a63ca29504628a Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 22:01:48 +1100 Subject: [PATCH 11/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 23 +++++--------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index a654be27d..7a10a504b 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -1031,27 +1031,16 @@ void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); - // TOP mode for recursive function cycles: set states to TOP and return immediately + // TOP mode for recursive function cycles: use recursiveCallPass to set + // all stores and return value to TOP, maintaining original semantics if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun())) { - // Process cycle head once to establish state - handleICFGNode(cycle_head); - - // Process cycle body once, setting all states - for (const ICFGWTOComp* comp : cycle->getWTOComponents()) + // Get the call node from callSiteStack (the call that entered this function) + if (!callSiteStack.empty()) { - if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast(comp)) - { - handleICFGNode(singleton->getICFGNode()); - } - else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) - { - handleICFGCycle(subCycle); - } + const CallICFGNode* callNode = callSiteStack.back(); + recursiveCallPass(callNode); } - - // Set cycle head state to TOP to represent unknown recursive result - abstractTrace[cycle_head] = abstractTrace[cycle_head].top(); return; } From 9c72e10b96ff6aab392510c2b617fdfcfd5c9689 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 22:27:12 +1100 Subject: [PATCH 12/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 46 +++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 7a10a504b..d62e11e0c 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -1009,24 +1009,46 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) abstractTrace[retNode] = abstractTrace[callNode]; } -/// handle wto cycle (loop) using widening/narrowing iteration +/// Handle WTO cycle (loop or recursive function) using widening/narrowing iteration. /// -/// This handles both regular loops and recursive function cycles. -/// The behavior depends on Options::HandleRecur() for recursive functions: +/// Widening is applied at the cycle head to ensure termination of the analysis. +/// The cycle head's abstract state is iteratively updated until a fixpoint is reached. /// -/// - TOP mode (recursive function cycle only): -/// Sets all values to TOP without iteration. This is the most conservative -/// but fastest approach. +/// == What is being widened == +/// The abstract state at the cycle head node, which includes: +/// - Variable values (intervals) that may change across loop iterations +/// - For example, a loop counter `i` starting at 0 and incrementing each iteration +/// +/// == Regular loops (non-recursive functions) == +/// All modes (TOP/WIDEN_ONLY/WIDEN_NARROW) behave the same for regular loops: +/// 1. Widening phase: Iterate until the cycle head state stabilizes +/// Example: for(i=0; i<100; i++) -> i widens to [0, +inf] +/// 2. Narrowing phase: Refine the over-approximation from widening +/// Example: [0, +inf] narrows to [0, 100] using loop condition +/// +/// == Recursive function cycles == +/// Behavior depends on Options::HandleRecur(): +/// +/// - TOP mode: +/// Does not iterate. Calls recursiveCallPass() to set all stores and +/// return value to TOP immediately. This is the most conservative but fastest. +/// Example: +/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); } +/// factorial(5) -> returns [-inf, +inf] /// /// - WIDEN_ONLY mode: -/// Iterates with widening until fixpoint. No narrowing phase. -/// For recursive functions, this gives upper bounds (e.g., [10000, +inf]). +/// Widening phase only, no narrowing for recursive functions. +/// The recursive function body is analyzed with widening until fixpoint. +/// Example: +/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); } +/// factorial(5) -> returns [10000, +inf] (widened upper bound) /// /// - WIDEN_NARROW mode: -/// Iterates with widening, then narrows to refine the result. -/// For recursive functions, this gives precise bounds (e.g., [10000, 10000]). -/// -/// For regular (non-recursive) loops, all modes use widening + narrowing. +/// Both widening and narrowing phases for recursive functions. +/// After widening reaches fixpoint, narrowing refines the result. +/// Example: +/// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); } +/// factorial(5) -> returns [10000, 10000] (precise after narrowing) void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); From e0d7674fb095ea0ac301bf3ac6381613dfdd39f3 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Mon, 2 Feb 2026 22:37:20 +1100 Subject: [PATCH 13/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 50 ------------------- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 13 +++-- 2 files changed, 10 insertions(+), 53 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 6aa365152..a7c9bf075 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -266,12 +266,6 @@ class AbstractInterpretation */ virtual void handleSVFStatement(const SVFStmt* stmt); - /** - * Sets all store values in the recursive function to TOP. - * This is called when skipping a recursive call in TOP mode. - * - * @param callnode CallICFGNode which calls a recursive function - */ virtual void setRecursiveCallStoresToTop(const CallICFGNode* callnode); @@ -359,52 +353,8 @@ class AbstractInterpretation virtual bool isIndirectCall(const CallICFGNode* callNode); virtual void indirectCallFunPass(const CallICFGNode* callNode); - /** - * Determines whether to skip (not inline) a call to a potentially recursive function. - * Returns true if the call should be skipped, false if normal inlining should proceed. - * - * This function is mode-independent: it only checks whether the call is a recursive - * callsite (within the same SCC). The actual handling of recursive functions is done - * uniformly via handleICFGCycle(), with mode-specific behavior inside that function. - * - * Example: - * int factorial(int n) { - * if (n <= 1) return 1; - * return n * factorial(n - 1); // inner call (recursive callsite) - * } - * int main() { - * return factorial(5); // outer call (entry into recursion) - * } - * - * For all modes (TOP/WIDEN_ONLY/WIDEN_NARROW): - * - Outer call (main -> factorial): NOT skipped, inlined into handleICFGCycle() - * - Inner call (factorial -> factorial): Skipped (back-edge of the cycle) - * - * The difference between modes is in handleICFGCycle(): - * - TOP: Sets all values to TOP without iteration. Result: [-inf, +inf] - * - WIDEN_ONLY: Widening iteration only. Result: [10000, +inf] - * - WIDEN_NARROW: Widening + narrowing. Result: [10000, 10000] - */ bool skipRecursiveCall(const CallICFGNode* callNode); - - /** - * Gets the callee function for a call node. - * For direct calls, returns callNode->getCalledFunction(). - * For indirect calls, resolves the callee through pointer analysis. - * Returns nullptr if callee cannot be determined. - */ const FunObjVar* getCallee(const CallICFGNode* callNode); - - /** - * Determines if narrowing should be applied for a cycle head. - * Called during the narrowing phase of handleICFGCycle(). - * - * For non-recursive functions (regular loops): always returns true. - * For recursive functions: depends on Options::HandleRecur(): - * - TOP mode: Should not reach here (asserts), TOP mode exits early in handleICFGCycle - * - WIDEN_ONLY: Returns false (skip narrowing) - * - WIDEN_NARROW: Returns true (apply narrowing) - */ bool shouldApplyNarrowing(const FunObjVar* fun); // there data should be shared with subclasses diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index d62e11e0c..42988c699 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -835,11 +835,13 @@ void AbstractInterpretation::extCallPass(const CallICFGNode *callNode) callSiteStack.pop_back(); } +/// Check if a function is recursive (part of a call graph SCC) bool AbstractInterpretation::isRecursiveFun(const FunObjVar* fun) { return recursiveFuns.find(fun) != recursiveFuns.end(); } +/// Check if a call node calls a recursive function bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode) { const FunObjVar *callfun = callNode->getCalledFunction(); @@ -849,6 +851,7 @@ bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode) return isRecursiveFun(callfun); } +/// Handle recursive call in TOP mode: set all stores and return value to TOP void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); @@ -868,6 +871,7 @@ void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode) abstractTrace[retNode] = as; } +/// Check if a call is a recursive callsite (within same SCC, not entry call from outside) bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar* callee) { @@ -875,6 +879,7 @@ bool AbstractInterpretation::isRecursiveCallSite(const CallICFGNode* callNode, nonRecursiveCallSites.end(); } +/// Get callee function: directly for direct calls, via pointer analysis for indirect calls const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode) { // Direct call: get callee directly from call node @@ -904,6 +909,7 @@ const FunObjVar* AbstractInterpretation::getCallee(const CallICFGNode* callNode) return SVFUtil::dyn_cast(func_var); } +/// Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode) { const FunObjVar* callee = getCallee(callNode); @@ -921,15 +927,15 @@ bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode) return isRecursiveCallSite(callNode, callee); } +/// Check if narrowing should be applied: always for regular loops, mode-dependent for recursion bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) { // Non-recursive functions (regular loops): always apply narrowing if (!isRecursiveFun(fun)) return true; - // Recursive functions: depends on mode - // Note: TOP mode handles recursive cycles separately in handleICFGCycle, - // so this should not be reached for recursive functions in TOP mode. + // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not + // TOP mode exits early in handleICFGCycle, so should not reach here switch (Options::HandleRecur()) { case TOP: @@ -1191,6 +1197,7 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) !getAbsStateFromTrace(stmt->getICFGNode())[IRGraph::NullPtr].isAddr()); } +/// Set all store values in a recursive function to TOP (used in TOP mode) void AbstractInterpretation::setRecursiveCallStoresToTop(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); From bf793f885fce4f23618cffb991ceb83f58d930f9 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Tue, 3 Feb 2026 15:40:31 +1100 Subject: [PATCH 14/18] rename handleICFGCycle --- svf/include/AE/Svfexe/AbstractInterpretation.h | 2 +- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index a7c9bf075..783489543 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -218,7 +218,7 @@ class AbstractInterpretation * * @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles */ - virtual void handleICFGCycle(const ICFGCycleWTO* cycle); + virtual void HandleLoopOrRecursion(const ICFGCycleWTO* cycle); /** * Handle a function using worklist algorithm diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 42988c699..208b4af7e 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -767,7 +767,7 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end()) { const ICFGCycleWTO* cycle = cycleHeadToCycle[node]; - handleICFGCycle(cycle); + HandleLoopOrRecursion(cycle); // Push nodes outside the cycle to the worklist std::vector cycleNextNodes = getNextNodesOfCycle(cycle); @@ -922,7 +922,7 @@ bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode) // For recursive functions, skip only recursive callsites (within same SCC). // Entry calls (from outside SCC) are not skipped - they are inlined so that - // handleICFGCycle() can analyze the function body. + // HandleLoopOrRecursion() can analyze the function body. // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW). return isRecursiveCallSite(callNode, callee); } @@ -935,7 +935,7 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) return true; // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not - // TOP mode exits early in handleICFGCycle, so should not reach here + // TOP mode exits early in HandleLoopOrRecursion, so should not reach here switch (Options::HandleRecur()) { case TOP: @@ -1055,7 +1055,7 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) /// Example: /// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); } /// factorial(5) -> returns [10000, 10000] (precise after narrowing) -void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) +void AbstractInterpretation::HandleLoopOrRecursion(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); @@ -1130,7 +1130,7 @@ void AbstractInterpretation::handleICFGCycle(const ICFGCycleWTO* cycle) else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) { // Handle nested cycle recursively - handleICFGCycle(subCycle); + HandleLoopOrRecursion(subCycle); } } } From 09e86d58f1353bbca7a9c3d39ce31b3307be17fb Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Tue, 3 Feb 2026 15:43:50 +1100 Subject: [PATCH 15/18] Continue to reduce the Options() handleRecur. (vibe-kanban 5f1e98b0) I think you could continue to narrow down the usage of OPtions::HandleREcur(). I mean maybe you could put the option check inside the function. For example, "// Check if this recursive call should be skipped if (shouldSkipRecursiveCall(callNode, funObjVar)) { // In TOP mode, set return value and stores to TOP // In WIDEN\_ONLY/WIDEN\_NARROW, just skip (WTO handles it) if (Options::HandleRecur() == TOP) handleSkippedRecursiveCall(callNode); return; } " maybe you should move the if check inside the function. You could try this way to reduce the Options::handleRecur as low as possible. --- .../AE/Svfexe/AbstractInterpretation.h | 5 +- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 58 ++----------------- 2 files changed, 7 insertions(+), 56 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 783489543..988dd7c05 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -348,10 +348,7 @@ class AbstractInterpretation virtual bool isRecursiveCall(const CallICFGNode* callNode); virtual void recursiveCallPass(const CallICFGNode *callNode); virtual bool isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar *); - virtual bool isDirectCall(const CallICFGNode* callNode); - virtual void directCallFunPass(const CallICFGNode* callNode); - virtual bool isIndirectCall(const CallICFGNode* callNode); - virtual void indirectCallFunPass(const CallICFGNode* callNode); + virtual void callFunPass(const CallICFGNode* callNode); bool skipRecursiveCall(const CallICFGNode* callNode); const FunObjVar* getCallee(const CallICFGNode* callNode); diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 208b4af7e..5353f16cc 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -804,16 +804,11 @@ void AbstractInterpretation::handleCallSite(const ICFGNode* node) { extCallPass(callNode); } - else if (isDirectCall(callNode)) - { - directCallFunPass(callNode); - } - else if (isIndirectCall(callNode)) + else { - indirectCallFunPass(callNode); + // Handle both direct and indirect calls uniformly + callFunPass(callNode); } - else - assert(false && "implement this part"); } else assert (false && "it is not call node"); @@ -950,51 +945,13 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) return false; } } - - -bool AbstractInterpretation::isDirectCall(const CallICFGNode *callNode) -{ - const FunObjVar *callfun =callNode->getCalledFunction(); - if (!callfun) - return false; - else - return !callfun->isDeclaration(); -} -void AbstractInterpretation::directCallFunPass(const CallICFGNode *callNode) +/// Handle direct or indirect call: get callee, process function body, set return state +void AbstractInterpretation::callFunPass(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); - abstractTrace[callNode] = as; - // Skip recursive call if applicable (returns true if skipped) - if (skipRecursiveCall(callNode)) - return; - - const FunObjVar *calleeFun = callNode->getCalledFunction(); - callSiteStack.push_back(callNode); - - // Use worklist-based function handling instead of recursive WTO component handling - const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(calleeFun); - handleFunction(calleeEntry); - - callSiteStack.pop_back(); - // handle Ret node - const RetICFGNode *retNode = callNode->getRetICFGNode(); - // resume ES to callnode - abstractTrace[retNode] = abstractTrace[callNode]; -} - -bool AbstractInterpretation::isIndirectCall(const CallICFGNode *callNode) -{ - const auto callsiteMaps = svfir->getIndirectCallsites(); - return callsiteMaps.find(callNode) != callsiteMaps.end(); -} - -void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) -{ - AbstractState& as = getAbsStateFromTrace(callNode); - - // Skip recursive call if applicable (returns true if skipped) + // Skip recursive callsites (within SCC); entry calls are not skipped if (skipRecursiveCall(callNode)) return; @@ -1003,14 +960,11 @@ void AbstractInterpretation::indirectCallFunPass(const CallICFGNode *callNode) return; callSiteStack.push_back(callNode); - abstractTrace[callNode] = as; - // Use worklist-based function handling instead of recursive WTO component handling const ICFGNode* calleeEntry = icfg->getFunEntryICFGNode(callee); handleFunction(calleeEntry); callSiteStack.pop_back(); - // handle Ret node const RetICFGNode* retNode = callNode->getRetICFGNode(); abstractTrace[retNode] = abstractTrace[callNode]; } From 76c2374a2eb6967dae09fa4d7640bde7451de624 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Wed, 4 Feb 2026 12:03:53 +1100 Subject: [PATCH 16/18] rename two functions in AbstractInterpretation (vibe-kanban 313b27a9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 你方便把 setRecursiveCallStoresToTop改名setTopToObjInRecursion 然后把callFunPass 改名HandleFunCall吗? 改名就行 --- svf/include/AE/Svfexe/AbstractInterpretation.h | 4 ++-- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 988dd7c05..55c12b116 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -266,7 +266,7 @@ class AbstractInterpretation */ virtual void handleSVFStatement(const SVFStmt* stmt); - virtual void setRecursiveCallStoresToTop(const CallICFGNode* callnode); + virtual void setTopToObjInRecursion(const CallICFGNode* callnode); /** @@ -348,7 +348,7 @@ class AbstractInterpretation virtual bool isRecursiveCall(const CallICFGNode* callNode); virtual void recursiveCallPass(const CallICFGNode *callNode); virtual bool isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar *); - virtual void callFunPass(const CallICFGNode* callNode); + virtual void HandleFunCall(const CallICFGNode* callNode); bool skipRecursiveCall(const CallICFGNode* callNode); const FunObjVar* getCallee(const CallICFGNode* callNode); diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 5353f16cc..2fa53183c 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -807,7 +807,7 @@ void AbstractInterpretation::handleCallSite(const ICFGNode* node) else { // Handle both direct and indirect calls uniformly - callFunPass(callNode); + HandleFunCall(callNode); } } else @@ -850,7 +850,7 @@ bool AbstractInterpretation::isRecursiveCall(const CallICFGNode *callNode) void AbstractInterpretation::recursiveCallPass(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); - setRecursiveCallStoresToTop(callNode); + setTopToObjInRecursion(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); if (retNode->getSVFStmts().size() > 0) { @@ -946,7 +946,7 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) } } /// Handle direct or indirect call: get callee, process function body, set return state -void AbstractInterpretation::callFunPass(const CallICFGNode *callNode) +void AbstractInterpretation::HandleFunCall(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); abstractTrace[callNode] = as; @@ -1152,7 +1152,7 @@ void AbstractInterpretation::handleSVFStatement(const SVFStmt *stmt) } /// Set all store values in a recursive function to TOP (used in TOP mode) -void AbstractInterpretation::setRecursiveCallStoresToTop(const CallICFGNode *callNode) +void AbstractInterpretation::setTopToObjInRecursion(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); const RetICFGNode *retNode = callNode->getRetICFGNode(); From 7fce76870f6459b92ef045df509878095eb26311 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Wed, 4 Feb 2026 14:52:09 +1100 Subject: [PATCH 17/18] rename two functions in AbstractInterpretation (vibe-kanban 313b27a9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 你方便把 setRecursiveCallStoresToTop改名setTopToObjInRecursion 然后把callFunPass 改名HandleFunCall吗? 改名就行 --- svf/include/AE/Svfexe/AbstractInterpretation.h | 4 ++-- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index 55c12b116..f17b09130 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -343,12 +343,12 @@ class AbstractInterpretation // helper functions in handleCallSite virtual bool isExtCall(const CallICFGNode* callNode); - virtual void extCallPass(const CallICFGNode* callNode); + virtual void handleExtCall(const CallICFGNode* callNode); virtual bool isRecursiveFun(const FunObjVar* fun); virtual bool isRecursiveCall(const CallICFGNode* callNode); virtual void recursiveCallPass(const CallICFGNode *callNode); virtual bool isRecursiveCallSite(const CallICFGNode* callNode, const FunObjVar *); - virtual void HandleFunCall(const CallICFGNode* callNode); + virtual void handleFunCall(const CallICFGNode* callNode); bool skipRecursiveCall(const CallICFGNode* callNode); const FunObjVar* getCallee(const CallICFGNode* callNode); diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 2fa53183c..3a4ee86bd 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -802,12 +802,12 @@ void AbstractInterpretation::handleCallSite(const ICFGNode* node) { if (isExtCall(callNode)) { - extCallPass(callNode); + handleExtCall(callNode); } else { // Handle both direct and indirect calls uniformly - HandleFunCall(callNode); + handleFunCall(callNode); } } else @@ -819,7 +819,7 @@ bool AbstractInterpretation::isExtCall(const CallICFGNode *callNode) return SVFUtil::isExtCall(callNode->getCalledFunction()); } -void AbstractInterpretation::extCallPass(const CallICFGNode *callNode) +void AbstractInterpretation::handleExtCall(const CallICFGNode *callNode) { callSiteStack.push_back(callNode); utils->handleExtAPI(callNode); @@ -946,7 +946,7 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) } } /// Handle direct or indirect call: get callee, process function body, set return state -void AbstractInterpretation::HandleFunCall(const CallICFGNode *callNode) +void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode) { AbstractState& as = getAbsStateFromTrace(callNode); abstractTrace[callNode] = as; From d3b4faee463f2f67462611b167fe46dcb160b917 Mon Sep 17 00:00:00 2001 From: bjjwwang Date: Wed, 4 Feb 2026 14:53:25 +1100 Subject: [PATCH 18/18] rename two functions in AbstractInterpretation (vibe-kanban 313b27a9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 你方便把 setRecursiveCallStoresToTop改名setTopToObjInRecursion 然后把callFunPass 改名HandleFunCall吗? 改名就行 --- svf/include/AE/Svfexe/AbstractInterpretation.h | 2 +- svf/lib/AE/Svfexe/AbstractInterpretation.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/svf/include/AE/Svfexe/AbstractInterpretation.h b/svf/include/AE/Svfexe/AbstractInterpretation.h index f17b09130..aea2e9d90 100644 --- a/svf/include/AE/Svfexe/AbstractInterpretation.h +++ b/svf/include/AE/Svfexe/AbstractInterpretation.h @@ -218,7 +218,7 @@ class AbstractInterpretation * * @param cycle WTOCycle which has weak topo order of basic blocks and nested cycles */ - virtual void HandleLoopOrRecursion(const ICFGCycleWTO* cycle); + virtual void handleLoopOrRecursion(const ICFGCycleWTO* cycle); /** * Handle a function using worklist algorithm diff --git a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp index 3a4ee86bd..5e413c9f2 100644 --- a/svf/lib/AE/Svfexe/AbstractInterpretation.cpp +++ b/svf/lib/AE/Svfexe/AbstractInterpretation.cpp @@ -767,7 +767,7 @@ void AbstractInterpretation::handleFunction(const ICFGNode* funEntry) if (cycleHeadToCycle.find(node) != cycleHeadToCycle.end()) { const ICFGCycleWTO* cycle = cycleHeadToCycle[node]; - HandleLoopOrRecursion(cycle); + handleLoopOrRecursion(cycle); // Push nodes outside the cycle to the worklist std::vector cycleNextNodes = getNextNodesOfCycle(cycle); @@ -917,7 +917,7 @@ bool AbstractInterpretation::skipRecursiveCall(const CallICFGNode* callNode) // For recursive functions, skip only recursive callsites (within same SCC). // Entry calls (from outside SCC) are not skipped - they are inlined so that - // HandleLoopOrRecursion() can analyze the function body. + // handleLoopOrRecursion() can analyze the function body. // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW). return isRecursiveCallSite(callNode, callee); } @@ -930,7 +930,7 @@ bool AbstractInterpretation::shouldApplyNarrowing(const FunObjVar* fun) return true; // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not - // TOP mode exits early in HandleLoopOrRecursion, so should not reach here + // TOP mode exits early in handleLoopOrRecursion, so should not reach here switch (Options::HandleRecur()) { case TOP: @@ -1009,7 +1009,7 @@ void AbstractInterpretation::handleFunCall(const CallICFGNode *callNode) /// Example: /// int factorial(int n) { return n <= 1 ? 1 : n * factorial(n-1); } /// factorial(5) -> returns [10000, 10000] (precise after narrowing) -void AbstractInterpretation::HandleLoopOrRecursion(const ICFGCycleWTO* cycle) +void AbstractInterpretation::handleLoopOrRecursion(const ICFGCycleWTO* cycle) { const ICFGNode* cycle_head = cycle->head()->getICFGNode(); @@ -1084,7 +1084,7 @@ void AbstractInterpretation::HandleLoopOrRecursion(const ICFGCycleWTO* cycle) else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast(comp)) { // Handle nested cycle recursively - HandleLoopOrRecursion(subCycle); + handleLoopOrRecursion(subCycle); } } }