Static Value-Flow Analysis
Loading...
Searching...
No Matches
AELoopRecursion.cpp
Go to the documentation of this file.
1//===- AELoopRecursion.cpp -- Loop / recursion handling for AE ---------//
2//
3// SVF: Static Value-Flow Analysis
4//
5// Copyright (C) <2013-> <Yulei Sui>
6//
7
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Affero General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU Affero General Public License for more details.
17
18// You should have received a copy of the GNU Affero General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20//
21//===----------------------------------------------------------------------===//
22//
23// Loop and recursion handling factored out of AbstractInterpretation.cpp.
24// Contains:
25// * The widen/narrow fixpoint driver (handleLoopOrRecursion)
26// * The dense base cycle helpers (getFullCycleHeadState /
27// widenCycleState / narrowCycleState — semi-sparse overrides live in
28// SparseAbstractInterpretation.cpp)
29// * Recursion-specific helpers (isRecursiveFun, isRecursiveCallSite,
30// skipRecursiveCall, skipRecursionWithTop, shouldApplyNarrowing)
31//
32
34#include "AE/Svfexe/AEWTO.h"
35#include "SVFIR/SVFIR.h"
36#include "WPA/Andersen.h"
37#include "Util/Options.h"
38
39using namespace SVF;
40using namespace SVFUtil;
41
42// =====================================================================
43// Recursion helpers
44// =====================================================================
45
51
55{
56 const RetICFGNode *retNode = callNode->getRetICFGNode();
57
58 // 1. Set return value to TOP
59 if (retNode->getSVFStmts().size() > 0)
60 {
61 if (const RetPE *retPE = SVFUtil::dyn_cast<RetPE>(*retNode->getSVFStmts().begin()))
62 {
63 if (!retPE->getLHSVar()->isPointer() &&
64 !retPE->getLHSVar()->isConstDataOrAggDataButNotNullPtr())
65 updateAbsValue(retPE->getLHSVar(), IntervalValue::top(), callNode);
66 }
67 }
68
69 // 2. Set all stores in callee's reachable BBs to TOP
70 if (retNode->getOutEdges().size() > 1)
71 {
73 return;
74 }
75 for (const SVFBasicBlock* bb : callNode->getCalledFunction()->getReachableBBs())
76 {
77 for (const ICFGNode* node : bb->getICFGNodeList())
78 {
79 for (const SVFStmt* stmt : node->getSVFStmts())
80 {
81 if (const StoreStmt* store = SVFUtil::dyn_cast<StoreStmt>(stmt))
82 {
83 const SVFVar* rhsVar = store->getRHSVar();
84 if (!rhsVar->isPointer() && !rhsVar->isConstDataOrAggDataButNotNullPtr())
85 {
86 const AbstractValue& addrs = getAbsValue(store->getLHSVar(), callNode);
87 if (addrs.isAddr())
88 {
90 for (const auto& addr : addrs.getAddrs())
91 as.store(addr, IntervalValue::top());
92 }
93 }
94 }
95 }
96 }
97 }
98
99 // 3. Copy callNode's state to retNode
101}
102
110
113{
115 if (!callee)
116 return false;
117
118 // Non-recursive function: never skip, always inline
120 return false;
121
122 // For recursive functions, skip only recursive callsites (within same SCC).
123 // Entry calls (from outside SCC) are not skipped - they are inlined so that
124 // handleLoopOrRecursion() can analyze the function body.
125 // This applies uniformly to all modes (TOP/WIDEN_ONLY/WIDEN_NARROW).
127}
128
131{
132 // Non-recursive functions (regular loops): always apply narrowing
133 if (!isRecursiveFun(fun))
134 return true;
135
136 // Recursive functions: WIDEN_NARROW applies narrowing, WIDEN_ONLY does not
137 // TOP mode exits early in handleLoopOrRecursion, so should not reach here
138 switch (Options::HandleRecur())
139 {
140 case TOP:
141 assert(false && "TOP mode should not reach narrowing phase for recursive functions");
142 return false;
143 case WIDEN_ONLY:
144 return false; // Skip narrowing for recursive functions
145 case WIDEN_NARROW:
146 return true; // Apply narrowing for recursive functions
147 default:
148 assert(false && "Unknown recursion handling mode");
149 return false;
150 }
151}
152
153// =====================================================================
154// Cycle state helpers (dense base)
155//
156// Dense default: trace[cycle_head] is the authoritative primary
157// storage, so the snapshot / write-back are trivial.
158// SemiSparseAbstractInterpretation overrides these to additionally
159// pull/scatter cycle ValVars from/to their def-sites.
160// =====================================================================
161
170
172 const AbstractState& prev, const AbstractState& cur, const ICFGCycleWTO* cycle)
173{
176 // Always write back (even at fixpoint) so cycle_head's trace holds the
177 // widened state for the upcoming narrowing phase.
178 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
180 return next == prev;
181}
182
184 const AbstractState& prev, const AbstractState& cur, const ICFGCycleWTO* cycle)
185{
186 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
187 if (!shouldApplyNarrowing(cycle_head->getFun()))
188 return true;
191 if (next == prev)
192 return true; // fixpoint
194 return false;
195}
196
197// =====================================================================
198// Cycle / recursion driver
199//
200// Handle a WTO cycle (loop or recursive function) using widening /
201// narrowing iteration. Widening at cycle head ensures termination.
202//
203// == What is being widened ==
204// The abstract state at the cycle head node, which includes:
205// - Variable values (intervals) that may change across loop iterations
206// - For example, a loop counter `i` starting at 0 and incrementing
207// each iteration
208//
209// == Regular loops (non-recursive functions) ==
210// All modes (TOP/WIDEN_ONLY/WIDEN_NARROW) behave the same for regular
211// loops:
212// 1. Widening phase: iterate until the cycle head state stabilizes
213// Example: for(i=0; i<100; i++) -> i widens to [0, +inf]
214// 2. Narrowing phase: refine the over-approximation from widening
215// Example: [0, +inf] narrows to [0, 100] using loop condition
216//
217// == Recursive function cycles ==
218// Behavior depends on Options::HandleRecur():
219//
220// - TOP: skip body entirely, set return + reachable stores
221// to TOP (most conservative, fastest)
222// - WIDEN_ONLY: widening only, no narrowing
223// factorial(5) -> [10000, +inf]
224// - WIDEN_NARROW: widening + narrowing
225// factorial(5) -> [10000, 10000]
226//
227// == Semi-sparse note ==
228// In semi-sparse mode ValVars live at their def-sites and do not flow
229// through cycle_head's merge. The cycle helpers in
230// SparseAbstractInterpretation.cpp gather them into the cycle_head
231// snapshot and scatter them back after each widen/narrow step so the
232// fixpoint can observe ValVar growth across iterations.
233// =====================================================================
234
236{
237 const ICFGNode* cycle_head = cycle->head()->getICFGNode();
238
239 // TOP mode for recursive function cycles: set all stores and return value to TOP
240 if (Options::HandleRecur() == TOP && isRecursiveFun(cycle_head->getFun()))
241 {
242 if (caller)
244 return;
245 }
246
247 // Iterate until fixpoint with widening/narrowing on the cycle head.
248 bool increasing = true;
250 for (u32_t cur_iter = 0;; cur_iter++)
251 {
252 if (cur_iter >= widen_delay)
253 {
254 // getFullCycleHeadState handles dense (returns trace[cycle_head])
255 // and semi-sparse (collects ValVars from def-sites) uniformly.
257
261
262 if (increasing)
263 {
264 if (widenCycleState(prev, cur, cycle))
265 {
266 increasing = false;
267 continue;
268 }
269 }
270 else
271 {
272 if (narrowCycleState(prev, cur, cycle))
273 break;
274 }
275 }
276 else
277 {
278 // Before widen_delay: process cycle head with gated pattern
281 }
282
283 // Process cycle body components (each with gated merge+handle)
284 for (const ICFGWTOComp* comp : cycle->getWTOComponents())
285 {
286 if (const ICFGSingletonWTO* singleton = SVFUtil::dyn_cast<ICFGSingletonWTO>(comp))
287 {
288 const ICFGNode* node = singleton->getICFGNode();
290 handleICFGNode(node);
291 }
292 else if (const ICFGCycleWTO* subCycle = SVFUtil::dyn_cast<ICFGCycleWTO>(comp))
293 {
294 if (mergeStatesFromPredecessors(subCycle->head()->getICFGNode()))
296 }
297 }
298 }
299}
item next
Definition cJSON.cpp:2224
newitem prev
Definition cJSON.cpp:2285
AndersenWaveDiff * getPointerAnalysis() const
Accessors for Andersen's results.
Definition AEWTO.h:56
const FunObjVar * getCallee(const CallICFGNode *callNode)
Get callee function: directly for direct calls, via pointer analysis for indirect calls.
AbstractState & getAbsState(const ICFGNode *node)
virtual bool narrowCycleState(const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
virtual AbstractState getFullCycleHeadState(const ICFGCycleWTO *cycle)
bool handleICFGNode(const ICFGNode *node)
Handle an ICFG node: execute statements; return true if state changed.
virtual void skipRecursionWithTop(const CallICFGNode *callNode)
virtual bool widenCycleState(const AbstractState &prev, const AbstractState &cur, const ICFGCycleWTO *cycle)
bool mergeStatesFromPredecessors(const ICFGNode *node)
bool skipRecursiveCall(const CallICFGNode *callNode)
Skip recursive callsites (within SCC); entry calls from outside SCC are not skipped.
virtual bool isRecursiveCallSite(const CallICFGNode *callNode, const FunObjVar *)
Check if caller and callee are in the same CallGraph SCC (i.e. a recursive callsite)
bool shouldApplyNarrowing(const FunObjVar *fun)
Check if narrowing should be applied: always for regular loops, mode-dependent for recursion.
virtual bool isRecursiveFun(const FunObjVar *fun)
Check if a function is recursive (part of a call graph SCC)
virtual const AbstractValue & getAbsValue(const ValVar *var, const ICFGNode *node)
bool hasAbsState(const ICFGNode *node)
Map< const ICFGNode *, AbstractState > abstractTrace
per-node trace; owned here
virtual void updateAbsValue(const ValVar *var, const AbstractValue &val, const ICFGNode *node)
virtual void handleLoopOrRecursion(const ICFGCycleWTO *cycle, const CallICFGNode *caller=nullptr)
Handle a WTO cycle (loop or recursive function) using widening/narrowing iteration.
virtual void updateAbsState(const ICFGNode *node, const AbstractState &state)
AbstractState narrowing(const AbstractState &other)
domain narrow with other, and return the narrowed domain
AbstractState widening(const AbstractState &other)
domain widen with other, and return the widened domain
AddressValue & getAddrs()
bool isAddr() const
static IntervalValue top()
Create the IntervalValue [-inf, +inf].
static const OptionMap< u32_t > HandleRecur
recursion handling mode, Default: TOP
Definition Options.h:246
static const Option< u32_t > WidenDelay
Definition Options.h:244
bool inSameCallGraphSCC(const FunObjVar *fun1, const FunObjVar *fun2)
Return TRUE if this edge is inside a PTACallGraph SCC, i.e., src node and dst node are in the same SC...
bool isInRecursion(const FunObjVar *fun) const
for isBitcode
Definition BasicTypes.h:70
WTONode< ICFG > ICFGSingletonWTO
Definition ICFGWTO.h:45
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76
unsigned u32_t
Definition GeneralType.h:47
WTOComponent< ICFG > ICFGWTOComp
Definition ICFGWTO.h:44