@@ -182,6 +182,121 @@ int leafAllocSize(FunctionCall call) {
182182 )
183183}
184184
185+ // ============================================================
186+ // Branch resolution: when the caller passes a constant, determine
187+ // which branch of a conditional in the callee is actually taken,
188+ // so that allocations on the not-taken branch are excluded.
189+ // ============================================================
190+
191+ /**
192+ * Holds if `predFunc` is a simple threshold predicate function: it has
193+ * a single return statement of the form `param < THRESHOLD`, returning
194+ * true when the parameter is below the threshold.
195+ */
196+ pragma [ noinline]
197+ predicate isLTThresholdPredicate ( Function predFunc , int paramIndex , int threshold ) {
198+ exists ( ReturnStmt ret , LTExpr lt |
199+ strictcount ( ReturnStmt r | r .getEnclosingFunction ( ) = predFunc ) = 1 and
200+ ret .getEnclosingFunction ( ) = predFunc and
201+ (
202+ lt = ret .getExpr ( )
203+ or
204+ lt = ret .getExpr ( ) .( Conversion ) .getExpr ( )
205+ ) and
206+ lt .getLeftOperand ( ) .( VariableAccess ) .getTarget ( ) = predFunc .getParameter ( paramIndex ) and
207+ threshold = constExprValue ( lt .getRightOperand ( ) )
208+ )
209+ }
210+
211+ /**
212+ * Holds if function `wrapper` directly passes its parameter `wrapperParamIdx`
213+ * as argument `calleeParamIdx` in a call to `callee`.
214+ */
215+ pragma [ noinline]
216+ predicate paramPassthrough ( Function wrapper , int wrapperParamIdx , Function callee , int calleeParamIdx ) {
217+ exists ( FunctionCall directCall |
218+ directCall .getEnclosingFunction ( ) = wrapper and
219+ directCall .getTarget ( ) = callee and
220+ directCall .getArgument ( calleeParamIdx ) .( VariableAccess ) .getTarget ( ) =
221+ wrapper .getParameter ( wrapperParamIdx )
222+ )
223+ }
224+
225+ /**
226+ * Gets the constant value that `targetFunc.getParameter(targetParamIdx)`
227+ * receives when `outerCall` is invoked, tracing constants through up to
228+ * two levels of wrapper functions that pass the parameter through.
229+ */
230+ pragma [ noinline]
231+ int resolvedParamValue ( FunctionCall outerCall , Function targetFunc , int targetParamIdx ) {
232+ // Direct: targetFunc is the immediate callee
233+ targetFunc = outerCall .getTarget ( ) and
234+ result = constExprValue ( outerCall .getArgument ( targetParamIdx ) )
235+ or
236+ // One level deep: outerCallee passes through to targetFunc
237+ exists ( Function outerCallee , int outerParamIdx |
238+ outerCallee = outerCall .getTarget ( ) and
239+ paramPassthrough ( outerCallee , outerParamIdx , targetFunc , targetParamIdx ) and
240+ result = constExprValue ( outerCall .getArgument ( outerParamIdx ) )
241+ )
242+ or
243+ // Two levels deep: outerCallee -> intermediate -> targetFunc
244+ exists (
245+ Function outerCallee , Function intermediate , int outerParamIdx , int intermediateParamIdx
246+ |
247+ outerCallee = outerCall .getTarget ( ) and
248+ paramPassthrough ( outerCallee , outerParamIdx , intermediate , intermediateParamIdx ) and
249+ paramPassthrough ( intermediate , intermediateParamIdx , targetFunc , targetParamIdx ) and
250+ result = constExprValue ( outerCall .getArgument ( outerParamIdx ) )
251+ )
252+ }
253+
254+ /**
255+ * Holds if `leafCall` in function `leafFunc` is on a branch that is provably
256+ * not taken when the outer call `outerCall` passes constant arguments.
257+ *
258+ * Detects the pattern: if (predicateFunc(param)) { ... } else { ... }
259+ * where predicateFunc is a simple `param < THRESHOLD` function, and the
260+ * constant value of param resolves the condition.
261+ */
262+ pragma [ noinline]
263+ predicate leafOnResolvedNotTakenBranch ( FunctionCall outerCall , FunctionCall leafCall ) {
264+ exists (
265+ Function leafFunc , IfStmt ifStmt , FunctionCall condCall , Function condFunc ,
266+ int condFuncParamIdx , int threshold , int leafFuncParamIdx , int paramValue ,
267+ BasicBlock condBB , BasicBlock notTakenBB
268+ |
269+ leafCall .getEnclosingFunction ( ) = leafFunc and
270+ ifStmt .getEnclosingFunction ( ) = leafFunc and
271+ // The condition is a call to a simple threshold predicate
272+ (
273+ condCall = ifStmt .getCondition ( )
274+ or
275+ condCall = ifStmt .getCondition ( ) .( Conversion ) .getExpr ( )
276+ ) and
277+ condFunc = condCall .getTarget ( ) and
278+ isLTThresholdPredicate ( condFunc , condFuncParamIdx , threshold ) and
279+ // The predicate receives a parameter of leafFunc
280+ condCall .getArgument ( condFuncParamIdx ) .( VariableAccess ) .getTarget ( ) =
281+ leafFunc .getParameter ( leafFuncParamIdx ) and
282+ // Resolve the constant value from the outer call
283+ paramValue = resolvedParamValue ( outerCall , leafFunc , leafFuncParamIdx ) and
284+ condBB = condCall .getBasicBlock ( ) and
285+ // Determine which branch is NOT taken
286+ (
287+ // param < threshold is TRUE → false branch not taken
288+ paramValue < threshold and
289+ notTakenBB = condBB .getAFalseSuccessor ( )
290+ or
291+ // param < threshold is FALSE → true branch not taken
292+ paramValue >= threshold and
293+ notTakenBB = condBB .getATrueSuccessor ( )
294+ ) and
295+ // The leaf call is on the not-taken branch
296+ bbDominates ( notTakenBB , leafCall .getBasicBlock ( ) )
297+ )
298+ }
299+
185300/**
186301 * Computes the total constant allocation size for a call to a function that
187302 * transitively calls `memory_heap_alloc` without its own ensure_free.
@@ -190,7 +305,9 @@ int leafAllocSize(FunctionCall call) {
190305 * - All fully-constant direct `memory_heap_alloc` sizes
191306 * - All `memory_heap_alloc(heap, constant + param)` where the caller passes
192307 * a constant for that parameter
193- * - All transitive wrapper function contributions (via reachableLeafAllocCall)
308+ * - All transitive wrapper function contributions (via reachableLeafAllocCall),
309+ * excluding leaf calls on branches that are provably not taken given the
310+ * caller's constant arguments
194311 */
195312pragma [ noinline]
196313int getConstAllocSize ( FunctionCall call ) {
@@ -208,7 +325,8 @@ int getConstAllocSize(FunctionCall call) {
208325 or
209326 exists ( FunctionCall leafCall |
210327 reachableLeafAllocCall ( callee , leafCall ) and
211- exists ( leafAllocSize ( leafCall ) )
328+ exists ( leafAllocSize ( leafCall ) ) and
329+ not leafOnResolvedNotTakenBranch ( call , leafCall )
212330 )
213331 ) and
214332 result =
@@ -221,10 +339,12 @@ int getConstAllocSize(FunctionCall call) {
221339 exists ( constExprValue ( call .getArgument ( paramIndex ) ) )
222340 | constPart + constExprValue ( call .getArgument ( paramIndex ) ) )
223341 +
224- // Sum transitive wrapper contributions via reachable leaf calls
342+ // Sum transitive wrapper contributions via reachable leaf calls,
343+ // excluding those on provably not-taken branches
225344 sum ( FunctionCall leafCall , int leafSize |
226345 reachableLeafAllocCall ( callee , leafCall ) and
227- leafSize = leafAllocSize ( leafCall )
346+ leafSize = leafAllocSize ( leafCall ) and
347+ not leafOnResolvedNotTakenBranch ( call , leafCall )
228348 | leafSize )
229349 )
230350}
0 commit comments