We also discuss some subtleties of interprocedural dependency structures highlighted by these changes.
7.1 Summary Tabulation for Memory Pressure
Given infinite memory, an incremental analysis could simply store all previously computed results forever, keeping them on hand in case they are ever needed. This is essentially what is formalized in Section
4. DAIGs are instantiated and added to
\(\mathcal {D}^\ast\) by
Q-Instantiate and, though their
contents are dirtied in response to edits by the semantics of Figure
8, they are never disposed of themselves. This design keeps the presentation simple, but it is infeasible in practice. This section extends the DSG formalism to explicitly account for memory-conserving operations, showing that soundness, termination, and from-scratch consistency are preserved under the extension.
We introduce a judgment form \(\mathcal {G}\dashrightarrow \mathcal {G}^{\prime }\) to describe transformations of analysis state that can be applied at will by an analyzer. It is possible of course to encode these operations as extensions to the query evaluation (\(\mathcal {G}\mathbin \vdash _{\varphi } \ell \Downarrow \varphi ^{\prime } \mathbin {;} \mathcal {G}^{\prime }\)) or edit (\(\mathcal {G} \mathbin \vdash _{\rho } n\Leftarrow s \mathbin ; \mathcal {G}^{\prime }\)) semantics, but such an approach ties the memory-freeing operations to analysis client interactions and complicates proofs of termination (by admitting infinite derivations that alternate between computing and discarding analysis facts).
The most direct such transformation is simply to discard procedure summary DAIGs that are not depended upon by others; this is, however, more heavy-handed than desired in most cases. In particular, note that DAIGs contain cached intermediate results at the granularity of individual semantic functions, but summary application in DSGs operates at the granularity of procedures.
Thus, instead of discarding an entire DAIG, an analyzer may exploit the fact that only its entry and exit abstract states are needed to summarize calls, by caching those two states and discarding any intermediate results.
We can extend the syntax and semantics of DSGs to express and reason about this direct summarization. Formally, we first add a summary table
\(\mathcal {T}\) to demanded summarization graphs, leaving the other components unchanged.
A summary table
\(\mathcal {T}\) is a collection of summary triples
\(\left\lbrace \varphi \right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime }\right\rbrace\) not backed by any DAIG in
\(\mathcal {D}^\ast\). For the most part, the operational semantics of Figures
7 and
8 just work with DSGs replaced by summary table-equipped analogues and tables
\(\mathcal {T}\) threaded through the rules accordingly.
However, some additional rules are needed to tabulate and apply summaries. As such, we reproduce the modified rules and provide the new rules in in Figure
10, highlighting the modifications relative to Section
4 in
green.
First, we add a rule Tabulate to the \(\mathcal {G}\dashrightarrow \mathcal {G}^{\prime }\) judgment form, allowing the analyzer to drop a fully solved DAIG \(\mathcal {D}\) and replace it by an equivalent Hoare-style summary triple at any point.
Note that the dependency map \(\Delta\) is unchanged when we drop a DAIG \(\mathcal {D}\) and tabulate the corresponding triple \(\left\lbrace \varphi \right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime }\right\rbrace\). Thus, any analysis fact that depended on \(\mathcal {D}\) now depends on the summary triple, and any DAIG or summary triple that contributed to \(\mathcal {D}\) also contributed to the summary triple.
Then, we add an additional inference rule S-Apply, which allows the tabulated Hoare triples in \(\mathcal {T}\) to resolve summary queries and be applied at procedure call sites.
The procedure summarization judgment
\(\mathcal {G}\mathbin \vdash \left\lbrace \varphi \right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime }\right\rbrace \mathbin ;\mathcal {G}^{\prime }\) effectively abstracts away the two different types of cached results in our summary table-equipped DSGs:
Summarize (Section
4) interprets fully solved DAIGs as summaries, while
S-Apply (Figure
10) interprets rows of the summary table
\(\mathcal {T}\) as summaries.
Next, we add a premise \(\left\lbrace \varphi \right\rbrace \mathbin {\rho ^\ell }\left\lbrace \_\right\rbrace \not\in \mathcal {T}\) to the Q-Instantiate rule, preventing the instantiation of a DAIG that shadows a summary already in \(\mathcal {T}\). Similarly, we tweak the definition of \(\lbrace \varphi _1,\dots ,\varphi _k\rbrace\) in the E-Delegate rule of the edit judgment, ensuring that when a procedure is edited, not only DAIGs but also summary triples over that procedure are dirtied. Both modifications consist of checking whether a summary exists in \(\mathcal {T}\) in addition to the original check whether it exists in \(\mathcal {D}^\ast\).
Finally, we add a rule D-Summary to the dirtying judgment, which is analogous to the D-DAIG rule but discards a summary triple instead of dirtying a DAIG. Note, though, that instead of dropping only those interprocedural dependencies \(\Delta ^{\prime }\) for now-dirtied intermediate results in \(\mathcal {D}\) (as in D-DAIG ), we drop all backward dependencies of the discarded triple.
Applying the Tabulate transformation allows a DSG-based analyzer to selectively coarsen its memoized results, achieving a significantly smaller memory footprint at the cost of fine-grained incremental reuse in cases where there is an edit inside a method whose DAIG has been dropped.
Metatheory. Moreover, extending demanded summarization graphs \(\mathcal {G}\) with summary tables \(\mathcal {T}\) as described here does not affect analysis results, since summaries in \(\mathcal {T}\) are created by Tabulate, applied by S-Apply, and dirtied by D-Intraproc-Summary under exactly the conditions that apply to corresponding DAIGs in \(\mathcal {D}^\ast\).
We do not reproduce the theorems and proofs in full here, as they are largely unchanged from Section
4, but they are available in Appendix B. The key modification required is an additional condition for semantic consistency (Definition
5.1), stating that whenever a summary triple is in
\(\mathcal {T}\), all of the dependency edges that would be required to compute that summary from scratch are in
\(\Delta\). By ensuring that summary triples in
\(\mathcal {T}\) are accompanied by the proper dependencies, we can guarantee that they are invalidated as needed in response to semantically relevant edits in other procedures.
Condensing DAIGs down to input/output summaries with
Tabulate provides a semantic foundation that can be used to implement memory conservation mechanisms without jeopardizing hard-won guarantees of soundness, termination, and from-scratch consistency. Similar approaches to reducing memory usage by intelligently discarding some analysis results have been shown to significantly improve performance in IFDS-based analysis frameworks [Arzt
2021].
Classic cache replacement strategies allow an interactive analysis engine to intelligently discard or condense DAIGs at set intervals or whenever memory usage crosses some threshold. For example, the least-recently or least-frequently used DAIGs can be reduced to two-state summaries, treating the DSG as an LRU/LFU cache of intermediate analysis results and reducing the overall memory footprint of the DSG without incurring any runtime cost unless/until the summary is affected by a program edit.
Beyond the practical applicability of these operations, the relative simplicity and straightforwardness of the extension indicates the generality and foundational nature of the DSG approach. For example, we believe that the DSG approach could be adapted to operate over custom relational domains rather than state domains, essentially replacing the DAIG intraprocedural analysis black-box with a different mechanism that produces such summaries, while tracking interprocedural dependencies in much the same way as we describe here.
7.2 Summary Weakening for Reuse
To apply a summary at a procedure call, the core DSG operational semantics laid out in Section
4 require that its precondition
exactly matches the callsite abstract state.
7 However, an analysis implementation may wish to maximize the reuse of previously computed summaries by applying a compatible one with a weaker-than-needed precondition instead. This is analogous to the use of monotonicity constraints in Datalog-based incremental analyses to reuse facts when there is an
\(\sqsubseteq\)-increasing change to some input [Szabó et al.
2018].
This optimization is enabled by adding the following inference rule to the summarization judgment \(\mathcal {G}\mathbin \vdash \left\lbrace \varphi \right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime }\right\rbrace \mathbin ;\mathcal {G}^{\prime }\).
Ignoring the demanded summarization graphs (i.e., everything before a turnstile or after a semicolon), this rule is precisely the familiar Hoare logic rule of consequence for preconditions [Hoare
1969]. Although it would be sound to do so, we do not include the dual postcondition rule (nor combine the two into one rule), as there is no benefit to applying it in our framework—it would simply produce less-precise analysis results.
The primed extensions of
\(\Delta\) and
\(\mathcal {T}\) in the conclusion of
S-Consequence serve to avoid a subtle issue: Without them, when this rule is used to weaken a summary, the
Q-Apply-Summary rule adds a dependency edge corresponding to the weakened (conclusion) summary rather than the stronger (premise) summary from which it was derived. As a result, when the premise summary is dirtied the analysis would unsoundly fail to propagate that change to the callsite where its weakened form was applied. This shortcoming is fully addressed, though, by adding the derived triple to
\(\mathcal {T}\), along with a dependency edge in
\(\Delta\) on the (weaker) premise triple, since dirtying will transitively propagate the change through the derived triple
\(\left\lbrace \varphi \right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime \prime }\right\rbrace\) in
\(\mathcal {T}^{\prime }\).
8 This does require that
\(\varphi\) and
\(\varphi\) be strictly ordered, as
\(\Delta ^{\prime }\) would contain spurious self-loops if they were equal. This is not a significant restriction, since “weakening” a summary to itself has no real benefit.
Weakening summaries by applying S-Consequence preserves the soundness of analysis results by the same reasoning that it is sound to apply the consequence rule in a standard Hoare logic. That is, the weaker triple \(\left\lbrace \varphi ^{\prime }\right\rbrace \mathbin {\rho }\left\lbrace \varphi ^{\prime \prime }\right\rbrace\) in its premise implies the stronger triple inferred as its conclusion: Since \(\varphi \sqsubset \varphi ^{\prime }\), any concrete state modelling \(\varphi\) also models \(\varphi ^{\prime }\) and is thus guaranteed to be mapped by the semantics of \(\rho\) to a state modelling \(\varphi ^{\prime \prime }\).
However, introducing the S-Consequence rule comes at the cost of from-scratch consistency for demanded analysis. To see why, consider an edit immediately before a procedure call that has been summarized with triple \(\lbrace \varphi _\text{pre}\rbrace \mathbin {\rho }\lbrace \varphi _\text{post}\rbrace\), and suppose that the edit results in a stronger precondition \(\varphi _\text{pre}^{\prime }\) for the call. It is sound to reuse the previously computed summary via S-Consequence, deriving the same postcondition \(\varphi _\text{post}\) for the call as before. However, it is possible that a from-scratch recomputation with the newly strengthened precondition may have produced a stronger summary \(\lbrace \vphantom{\varphi _\text{pre}}\varphi _\text{pre}^{\prime }\vphantom{\varphi _\text{pre}}\rbrace \mathop \rho \lbrace \vphantom{\varphi _\text{post}}\varphi _\text{post}^{\prime }\vphantom{\varphi _\text{post}}\rbrace\), so from-scratch consistency has been violated.
Thus, weakening of summaries represents a tradeoff and a tunable parameter for the design of practical analysis tools based on demanded summarization. In some circumstances, the benefit (i.e., computational savings due to increased summary reuse) may be well worth the cost of weaker precision guarantees; in others, the predictable behavior and maximal precision of a from-scratch consistent demanded analysis are more important.