Papers
Topics
Authors
Recent
Search
2000 character limit reached

Prefer Implicit Pointees (PIP)

Updated 5 July 2026
  • Prefer Implicit Pointees (PIP) is a technique that refines Andersen-style points-to analysis for incomplete C programs by implicitly tracking external memory using six one-bit flags.
  • The method reduces the need for explicit unknowns, cutting down computational overhead and achieving up to 15x speedup with an additional 1.9x gain in practical compiler settings.
  • By preserving soundness and precision while minimizing explicit memory propagation, PIP enhances alias analysis efficiency and scales well in production compiler pipelines.

Searching arXiv for the cited papers to ground the article in the current literature. Prefer Implicit Pointees (PIP) is a technique for making Andersen-style points-to analysis sound and practical for incomplete C programs. It was introduced in “PIP: Making Andersen's Points-to Analysis Sound and Practical for Incomplete C Programs” (Krogstie et al., 8 Dec 2025). The central problem is that production compilers routinely analyze files in isolation, link against external libraries, and encounter missing function bodies or global definitions, whereas classical Andersen analysis is sound only for complete programs. PIP addresses this mismatch by tracking memory locations and pointers that are accessible from external modules, but doing so implicitly in the constraint graph rather than by materializing a monolithic unknown memory object and its full Cartesian product of constraints. In the reported compiler setting, implicit pointee tracking yields a solver that is 15×15\times faster than any configuration using explicit unknowns, and PIP contributes an additional 1.9×1.9\times speedup while preserving soundness and improving alias-analysis utility.

1. Incomplete-program points-to analysis

Andersen’s classical points-to analysis is flow-insensitive and inclusion-based. Each pointer variable pp has a points-to set Sol(p)\mathrm{Sol}(p), and the analysis generates constraints of the form

p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,

corresponding respectively to address-of, copy, load, and store. The least solution Sol\mathrm{Sol} is intended to satisfy the standard soundness condition: whenever pp may target xx at runtime, xSol(p)x \in \mathrm{Sol}(p).

The difficulty is that this formulation is only sound on complete programs, namely programs in which every function body and global variable definition is present. Real-world C compilation is often per-file or per-module, may link to external libraries, and may involve dynamic loading. In that setting, unknown functions or globals may read, write, or arbitrarily manipulate pointer-valued memory. State-of-the-art points-to analyses therefore require summary functions to describe missing program parts, but such summaries are rarely available in production compilers, where soundness and efficiency are both non-negotiable.

A common remedy is to introduce a special abstract location Unknown and add constraints such as

p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,

so that arbitrary external stores and loads are conservatively modeled. This ensures soundness, but it also causes every pointer to become related to Unknown and Unknown to become related to every memory location. The paper identifies this as the practical failure mode of explicit unknown modeling: the number of pointer–pointee pairs explodes, and both precision and solver performance degrade sharply (Krogstie et al., 8 Dec 2025).

2. Implicit external memory and the six flags

PIP replaces the monolithic Unknown location by impliciting its Cartesian product constraints. The representation introduces a logical non-variable symbol 1.9×1.9\times0, interpreted as “external memory,” and six 1.9×1.9\times1-bit flags for each constraint variable 1.9×1.9\times2, where 1.9×1.9\times3 is the set of pointers and 1.9×1.9\times4 is the set of abstract memory locations.

The six flags are:

  • 1.9×1.9\times5: 1.9×1.9\times6 may point to all externally accessible memory.
  • 1.9×1.9\times7: 1.9×1.9\times8 is externally accessible.
  • 1.9×1.9\times9: pointees of pp0 become externally accessible.
  • pp1: store of scalar to pp2.
  • pp3: load of scalar from pp4.
  • pp5: pp6 is an imported external function.

This formulation distinguishes explicit from implicit pointees. An explicit pointee pp7 is represented by the base constraint pp8. By contrast, the externally accessible set

pp9

is not materialized per pointer; instead, Sol(p)\mathrm{Sol}(p)0 denotes that Sol(p)\mathrm{Sol}(p)1 may target all of Sol(p)\mathrm{Sol}(p)2. The design goal is not to weaken the semantics of the analysis, but to avoid explicitly building the Sol(p)\mathrm{Sol}(p)3 cross-product that would arise from an explicit unknown node.

The extended inference system combines standard inclusion constraints with six rules over the new flags. The main propagation rule is transitive: if Sol(p)\mathrm{Sol}(p)4 and there is an edge Sol(p)\mathrm{Sol}(p)5, then Sol(p)\mathrm{Sol}(p)6; in parallel, if there is an edge Sol(p)\mathrm{Sol}(p)7 and an explicit fact Sol(p)\mathrm{Sol}(p)8, then Sol(p)\mathrm{Sol}(p)9. Additional rules capture external interaction. If p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,0 and p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,1, then p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,2 and p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,3, modeling external loads and stores on escaped memory. If p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,4 and p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,5, then p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,6, propagating escape of p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,7. If p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,8, then p{a},pq,pq,pq,p \supseteq \{a\},\qquad p \supseteq q,\qquad p \supseteq *q,\qquad *p \supseteq q,9; if Sol\mathrm{Sol}0, then Sol\mathrm{Sol}1. For imported calls, if Sol\mathrm{Sol}2, and either Sol\mathrm{Sol}3 or Sol\mathrm{Sol}4, then each argument Sol\mathrm{Sol}5 is marked Sol\mathrm{Sol}6 and the result Sol\mathrm{Sol}7.

3. The Prefer Implicit Pointees technique in the solver

The implementation is a standard worklist solver over the constraint graph. Each node Sol\mathrm{Sol}8 maintains a set Sol\mathrm{Sol}9 of explicit pointees together with the six pp0-bit flags. The solver iterates to a fixed point, but PIP adds three operational checks that reduce the amount of explicit information that needs to be propagated.

First, PIP performs backpropagation of pp1 via any out-edge pp2. If pp3 is not yet marked pp4, but some out-neighbor pp5 has pp6, then pp7 is set and incoming neighbors are reprocessed. The paper states that this simplifies detection of global escape.

Second, once both pp8 and pp9 hold, the solver knows that xx0, and explicit pointees can be removed. For every xx1, the solver sets xx2, then clears xx3. This dynamic clearing is central to the “prefer implicit” principle: when a node already implicitly covers all relevant targets, retaining explicit pairs becomes redundant.

Third, PIP skips explicit propagation along an edge xx4 when both xx5 and xx6 hold. Under that condition, every explicit pointee of xx7 would already be implicit for xx8, so material propagation is unnecessary. The paper presents this as the main solver-level mechanism by which PIP further reduces the use of explicit pointees.

The resulting workflow is still an Andersen-style constraint solve, but with a representation that suppresses redundant explicit facts as soon as the implicit abstraction is sufficient. This is the specific contribution of Prefer Implicit Pointees, as distinct from implicit pointee tracking in general.

4. Soundness, least fixed point, and asymptotic properties

The soundness claim is exact: the six new implicit rules simulate the effect of having an explicit Unknown node with constraints such as Unknown ⊇ {escaped x for all x}, p ⊇ Unknown for all pointers p of unknown origin, and *Unknown ⊇ q. Accordingly, whenever xx9 may target xSol(p)x \in \mathrm{Sol}(p)0 at runtime, either xSol(p)x \in \mathrm{Sol}(p)1 or both xSol(p)x \in \mathrm{Sol}(p)2 and xSol(p)x \in \mathrm{Sol}(p)3 hold. The union of explicit and implicit solutions is therefore sound.

The paper also states a relative completeness property: the solver computes the least fixed point of the flag-equipped rules in the corresponding lattice of constraints, and no valid pointer–pointee relation is lost. The point of the implicit representation is not approximation by omission, but equivalence to the explicit-unknown semantics without explicit construction of its cross-product.

For complexity, let xSol(p)x \in \mathrm{Sol}(p)4, let xSol(p)x \in \mathrm{Sol}(p)5 be the number of constraint edges, and let xSol(p)x \in \mathrm{Sol}(p)6 be the number of explicit pointee pairs. An explicit solver can take up to xSol(p)x \in \mathrm{Sol}(p)7 per propagation, while xSol(p)x \in \mathrm{Sol}(p)8 may be xSol(p)x \in \mathrm{Sol}(p)9 in the worst case. The implicit solver builds only p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,0 constraints plus flags. PIP is then described as ensuring that the solver never pays for the p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,1 cross-product. A plausible implication is that the practical benefit of PIP is tied not only to a smaller graph, but to the ability to stop explicit set growth precisely when external-memory coverage has already been established.

5. Empirical results in a compiler setting

The evaluation uses the jlm compiler front-end on LLVM18 IR, with nine SPEC CPU2017 C programs and four open-source programs. Each C file is analyzed in isolation, explicitly placing the analysis in the incomplete-program scenario (Krogstie et al., 8 Dec 2025).

For solver runtime per file, measured in p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,2, the reported configurations are as follows. EP Oracle (best explicit) has mean p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,3, p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,4 p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,5, p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,6 p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,7, and max p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,8. IP + WL([FIFO](https://www.emergentmind.com/topics/fog-invariant-feature-learning-fifo)) (implicit only) has mean p{Unknown},Unknownq,p \supseteq \{\mathrm{Unknown}\},\qquad *\mathrm{Unknown} \supseteq q,9, 1.9×1.9\times00 1.9×1.9\times01, 1.9×1.9\times02 1.9×1.9\times03, and max 1.9×1.9\times04. IP + WL(FIFO) + PIP has mean 1.9×1.9\times05, 1.9×1.9\times06 1.9×1.9\times07, 1.9×1.9\times08 1.9×1.9\times09, and max 1.9×1.9\times10. The reported aggregate conclusions are that implicit tracing alone is 1.9×1.9\times11 faster than explicit tracking, and that adding PIP gives a further 1.9×1.9\times12 speedup over the best non-PIP implicit solver. The same results are summarized as collapsing the pathological max from 1.9×1.9\times13 min to 1.9×1.9\times14 ms.

Precision is evaluated through an alias-analysis client performing load/store conflict queries. When the final points-to information is combined with LLVM’s BasicAA, the number of MayAlias answers drops by 1.9×1.9\times15 on average compared to BasicAA alone. The paper presents this as evidence that the precision of the incomplete-program analysis is sufficient for typical compiler clients.

The memory result is qualitative but direct: the analysis is reported to be scalable in terms of memory, making it suitable for optimizing compilers in practice. This aligns with the representation claim that the only storage overhead per node is six bits in addition to the explicit sparse points-to set.

6. Integration into compiler pipelines

The implementation guidance is concrete. A worklist solver should maintain per-node bitflags for the six implicit constraints and extend existing push/pop propagation logic with the implicit rules. The queue discipline may be simple: a FIFO or LIFO worklist is sufficient, because PIP makes sophisticated iteration orders such as LRF, 2LRF, and topological ordering unnecessary in the file-level scenario.

For memory representation, 1.9×1.9\times16 may be stored as a sparse hash or bit-vector. The rationale given is that PIP keeps 1.9×1.9\times17 small. The use of 1.9×1.9\times18 also permits selective precision for well-known library functions such as malloc and memcpy, supplanting the conservative imported-call rule when precise behavior is available.

The compiler-specific guidance is to enable PIP in the front end or IR optimizer stage, before inter-procedural instrumentation. If a compiler already defers to per-module alias analysis, the explicit Unknown solver can simply be replaced by the PIP solver. For large modules with millions of pointers, the worklist may be chunked or pointers parallelized by IR, and the paper attributes the feasibility of such tuning to PIP’s linear memory footprint. It also reports that, in practice, a single-threaded solve per file is 1.9×1.9\times19 ms at O0, making the analysis easily amortized by multi-file build parallelism.

7. Relation to other implicit-pointee frameworks

A related but distinct line of work appears in “Flow- and Context-Sensitive Points-to Analysis using Generalized Points-to Graphs” (Gharat et al., 2016). There, unknown locations are also left implicit, but the mechanism is different. Instead of a global external-memory symbol with flags, the representation uses generalized points-to facts of the form

1.9×1.9\times20

read as: every location reached by 1.9×1.9\times21 dereferences of 1.9×1.9\times22 may hold the address of every location reached by 1.9×1.9\times23 dereferences of 1.9×1.9\times24. By carrying only the indirection-level counts 1.9×1.9\times25 and 1.9×1.9\times26, intermediate unknown locations are never named explicitly. Edge composition then reduces generalized facts to classical facts, and may versus must updates are distinguished through cardinality conditions on the source set.

The GPG framework is bottom-up, interprocedural, and summary-based. A procedure summary is a finite set of generalized edges. The size of a generalized points-to graph is linearly bounded by the number of variables and independent of the number of statements in the procedure; the formal bound given is 1.9×1.9\times27, with the paper reporting that in practice the summaries are compact, typically 1.9×1.9\times28. Empirically, GPGs scale to 1.9×1.9\times29 kLoC on SPEC CPU2006, compared to 1.9×1.9\times30 kLoC reported by liveness-based FCPA. A plausible implication is that implicit-pointee representations form a broader methodological family: PIP uses per-node flags to make incomplete-program Andersen analysis sound and practical at file scope, whereas GPGs use indirection-level counts to build flow- and context-sensitive summary flow functions without placeholders.

Topic to Video (Beta)

No one has generated a video about this topic yet.

Whiteboard

No one has generated a whiteboard explanation for this topic yet.

Follow Topic

Get notified by email when new papers are published related to Prefer Implicit Pointees (PIP).