agentleFS
rainoftimerainoftime/pl-skills/alias-and-points-to-analysis/SKILL.md

alias-and-points-to-analysis

Skill17 starsChanged 9 months agoApache-2.0View on GitHub

Implements alias and points-to analysis for pointer programs. Use for: (1) Optimizing compilers, (2) Verifying memory safety, (3) Program understanding, (4) Parallelization.

The file

We split this skill into the rules it gives an agent

7 rules. About 0 are common, 7 are distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈7

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Start with Andersen's for precision, Steensgaard's for speed

    line 369
  2. Distinctive:

    Use allocation-site abstraction for heap

    line 370
  3. Distinctive:

    Add field sensitivity for structures

    line 371
  4. Distinctive:

    Consider context sensitivity for functions

    line 372
  5. Distinctive:

    Use flow-insensitive as default

    line 377
  6. Distinctive:

    When may-alias info is not needed

    line 414
  7. Distinctive:

    Imprecision: Must-alias vs may-alias trade-off

    line 426

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Alias and Points-To Analysis

Implements interprocedural alias and points-to analysis for pointer programs.

When to Use

  • Compiler optimization
  • Memory safety verification
  • Program understanding
  • Security analysis
  • Parallelization
  • Bug detection

What This Skill Does

  1. Points-to analysis - Compute pointer targets
  2. Alias detection - Find aliased locations
  3. Interprocedural - Across function calls
  4. Flow-sensitive - With control flow

Analysis Types

TypePrecisionSpeed
Must-aliasDefinitely aliasedFast
May-aliasPossibly aliasedMedium
No-aliasDefinitely not aliasedMedium

Key Concepts

ConceptDescription
Points-toSet of locations a pointer may reach
Points-To SetMemory locations a pointer may reference
AliasTwo references may denote same location
Must-aliasAlways aliased
May-aliasPossibly aliased
Flow SensitivityDifferent results at different program points
Context SensitivityDifferent results for different call contexts
Allocation SiteAbstract location representing all objects created there
Inclusion-basedAndersen's (subset constraints)
Unification-basedSteensgaard's (union-find)

Implementation

from dataclasses import dataclass, field
from typing import Dict, Set, List, Optional, Tuple

@dataclass
class Location:
    """Memory location"""
    name: str
    offset: Optional[int] = None

@dataclass
class PointsToSet:
    """Set of locations a pointer may point to"""
    targets: Set[Location] = field(default_factory=set)

class AliasAnalysis:
    """Flow-insensitive alias analysis"""
    
    def __init__(self):
        self.pts: Dict[str, PointsToSet] = {}  # pointer -> points-to set
        self.globals: Set[str] = set()
        self.heap: Set[str] = set()
    
    def analyze(self, program: 'Program') -> Dict[Tuple[str, str], str]:
        """
        Compute alias relationships
        
        Returns: {(x, y): alias_type}
                 where alias_type in {MUST, MAY, NO}
        """
        
        # Build call graph
        self.call_graph = self.build_call_graph(program)
        
        # Initialize points-to
        self.initialize(program)
        
        # Iterative analysis
        changed = True
        while changed:
            changed = self.iterate(program)
        
        # Compute alias information
        return self.compute_aliases()
    
    def iterate(self, program: 'Program') -> bool:
        """Single iteration of analysis"""
        
        changed = False
        
        for func in program.functions:
            for stmt in func.body:
                changed |= self.analyze_stmt(stmt)
        
        # Interprocedural
        for call in program.calls:
            changed |= self.analyze_call(call)
        
        return changed
    
    def analyze_stmt(self, stmt: 'Stmt') -> bool:
        """Analyze single statement"""
        
        changed = False
        
        match stmt:
            case Assign(x, y):
                # x = y: copy points-to
                if y in self.pts:
                    old = self.pts.get(x, PointsToSet())
                    self.pts[x] = old
                    if old != self.pts.get(x):
                        changed = True
            
            case AddressOf(x, y):
                # x = &y: points to y
                self.pts[x] = PointsToSet({Location(y)})
                changed = True
            
            case Load(x, y):
                # x = *y: dereference
                if y in self.pts:
                    targets = self.pts[y].targets
                    new_pts = set()
                    for loc in targets:
                        if isinstance(loc, HeapLocation):
                            new_pts.add(loc)
                    if x not in self.pts or self.pts[x].targets != new_pts:
                        self.pts[x] = PointsToSet(new_pts)
                        changed = True
            
            case Store(x, y):
                # *x = y
                if x in self.pts:
                    # Points-to of y may be stored
                    pass
            
            case Alloc(x):
                # x = new T: heap allocation
                heap_loc = HeapLocation(f"heap_{len(self.heap)}")
                self.pts[x] = PointsToSet({heap_loc})
                self.heap.add(heap_loc.name)
                changed = True
        
        return changed
    
    def compute_aliases(self) -> Dict[Tuple[str, str], str]:
        """Compute alias pairs"""
        
        aliases = {}
        
        pointers = list(self.pts.keys())
        
        for i, p1 in enumerate(pointers):
            for p2 in pointers[i+1:]:
                alias_type = self.check_alias(p1, p2)
                if alias_type != "NO":
                    aliases[(p1, p2)] = alias_type
                    aliases[(p2, p1)] = alias_type
        
        return aliases
    
    def check_alias(self, x: str, y: str) -> str:
        """Check alias relationship"""
        
        pts_x = self.pts.get(x, PointsToSet()).targets
        pts_y = self.pts.get(y, PointsToSet()).targets
        
        if not pts_x or not pts_y:
            return "NO"
        
        # Must-alias: exactly same points-to
        if pts_x == pts_y and len(pts_x) == 1:
            return "MAY"
        
        # May-alias: intersection non-empty
        if pts_x & pts_y:
            return "MAY"
        
        return "NO"

Andersen's Analysis

class AndersensAnalysis(AliasAnalysis):
    """Andersen's inclusion-based analysis (O(n³))"""
    
    def analyze(self, program: 'Program') -> Dict:
        """Andersen-style analysis"""
        
        # Constraint generation
        constraints = self.generate_constraints(program)
        
        # Solve constraints iteratively
        return self.solve_constraints(constraints)
    
    def generate_constraints(self, program: 'Program') -> List:
        """Generate points-to constraints"""
        
        constraints = []
        
        for func in program.functions:
            for stmt in func.body:
                match stmt:
                    case Assign(x, y):
                        # x ⊆ pts(y)
                        constraints.append(('subset', x, y))
                    
                    case AddressOf(x, y):
                        # pts(x) ⊇ {y}
                        constraints.append(('points-to', x, y))
                    
                    case Load(x, y):
                        # For each p in pts(y): pts(x) ⊆ pts(p)
                        constraints.append(('load', x, y))
                    
                    case Store(x, y):
                        # For each p in pts(x): pts(p) ⊆ pts(y)
                        constraints.append(('store', x, y))
        
        return constraints
    
    def solve_constraints(self, constraints: List) -> Dict:
        """Solve constraints by fixed point"""
        
        # Initialize
        self.pts = {v: PointsToSet() for v in get_all_vars()}
        
        # Iterate until fixed point
        while True:
            old_pts = dict(self.pts)
            
            for constraint in constraints:
                self.apply_constraint(constraint)
            
            if self.pts == old_pts:
                break
        
        return self.compute_aliases()
    
    def apply_constraint(self, constraint):
        """Apply single constraint"""
        
        kind, x, y = constraint
        
        match kind:
            case 'subset':
                # pts(x) ⊆ pts(y)
                self.pts[y].targets |= self.pts[x].targets
            
            case 'points-to':
                # pts(x) ⊇ {y}
                self.pts[x].targets.add(Location(y))
            
            case 'load':
                # x = *y
                for loc in self.pts.get(y, PointsToSet()).targets:
                    self.pts[x].targets |= self.pts.get(loc.name, PointsToSet()).targets
            
            case 'store':
                # *x = y
                for loc in self.pts.get(x, PointsToSet()).targets:
                    self.pts[loc.name].targets |= self.pts.get(y, PointsToSet()).targets

Steensgaard's Analysis

class SteensgaardAnalysis(AliasAnalysis):
    """Steensgaard's unification-based analysis (almost linear)"""
    
    def analyze(self, program: 'Program') -> Dict:
        """Unification-based analysis"""
        
        # Union-find for locations
        self.parent = {}
        self.rank = {}
        
        # Initialize
        for v in get_all_vars():
            self.make_set(v)
        
        # Unification
        for func in program.functions:
            for stmt in func.body:
                self.unify_stmt(stmt)
        
        # Extract points-to from unions
        return self.extract_points_to()
    
    def make_set(self, x):
        self.parent[x] = x
        self.rank[x] = 0
    
    def find(self, x):
        if self.parent[x] != x:
            self.parent[x] = self.find(self.parent[x])
        return self.parent[x]
    
    def union(self, x, y):
        """Unify two locations"""
        
        px, py = self.find(x), self.find(y)
        
        if px == py:
            return
        
        if self.rank[px] < self.rank[py]:
            px, py = py, px
        
        self.parent[py] = px
        if self.rank[px] == self.rank[py]:
            self.rank[px] += 1
    
    def unify_stmt(self, stmt):
        """Unify based on statement"""
        
        match stmt:
            case Assign(x, y):
                self.union(x, y)
            
            case Load(x, y):
                # Unify x with all of y's targets
                pass
            
            case Store(x, y):
                pass

Applications

  • Dead store elimination
  • Scalar replacement
  • Lock elision
  • Memory safety
  • Parallelization (dependence analysis)
  • Bug detection (null pointer, use-after-free)
  • Security analysis (taint tracking)
  • IDE features (find references)
  • Garbage collector implementation

Tips

  • Start with Andersen's for precision, Steensgaard's for speed
  • Use allocation-site abstraction for heap
  • Add field sensitivity for structures
  • Consider context sensitivity for functions
  • Profile before investing in precision
  • Choose precision vs speed tradeoff
  • Handle function calls carefully
  • Consider context sensitivity
  • Use flow-insensitive as default

Related Skills

  • constant-propagation-pass - Dataflow analysis
  • dead-code-eliminator - Dead code elimination
  • dataflow-analysis-framework - Analysis framework
  • escape-analysis - Uses points-to for heap analysis
  • taint-analysis - Combines with points-to

Canonical References

ReferenceWhy It Matters
Andersen, "Program Analysis and Specialization for the C Programming Language" (PhD thesis, 1994)Andersen's inclusion-based analysis
Steensgaard, "Points-to Analysis in Almost Linear Time" (POPL 1996)Unification-based analysis
Emami, Ghiya, Hendren, "Context-Sensitive Interprocedural Points-to Analysis" (PLDI 1994)Context-sensitive analysis
Landi, "Undecidability of Static Analysis" (LOPLAS 1992)Theoretical limits
Hardekopf & Lin, "The Ant and the Grasshopper" (PLDI 2007)Fast inclusion-based analysis

Tradeoffs and Limitations

Analysis Approach Tradeoffs

ApproachPrecisionComplexityNotes
AndersenHighO(n³)Inclusion-based
SteensgaardLow~O(n)Unification
Flow-sensitiveHighExpensivePer-statement
Flow-insensitiveLowFastPer-program

When NOT to Use This Skill

  • For type-safe languages: Reference types make it simpler
  • For low-level code: Manual analysis may be needed
  • For small programs: May be overkill
  • Languages without pointers/references
  • When may-alias info is not needed
  • Performance-critical compilation (use fast analysis)

Complexity Considerations

  • Andersen: O(n³) worst case, practical
  • Steensgaard: Almost linear
  • Context-sensitive: Exponential without summarization

Limitations

  • Undecidability: Full alias analysis is undecidable
  • Imprecision: Must-alias vs may-alias trade-off
  • Interprocedural: Requires call-graph
  • Scalability: Flow-sensitivity expensive
  • Language features: Exceptions, reflection complicate
  • Array aliasing: Hard to handle precisely
  • Heap modeling is approximate

Assessment Criteria

A high-quality implementation should have:

CriterionWhat to Look For
SoundnessIncludes all possible targets
PrecisionMinimizes spurious aliases
EfficiencyReasonable runtime
CorrectnessMatches expected results

Quality Indicators

✅ Good: Sound, reasonably precise, efficient ⚠️ Warning: Too imprecise (all aliases) or too slow ❌ Bad: Unsound (misses possible targets)

Research Tools & Artifacts

Pointer analysis tools:

ToolApplicationWhat to Learn
SootJavaPointer analysis
WALAJavaFramework
LLVMC/C++Alias analysis

Research Frontiers

1. Field-Sensitive Analysis

  • Goal: Per-field precision
  • Approach: Structure splitting

2. Demand-Driven Analysis

  • Goal: Compute on-demand
  • Approach: Query-based

Implementation Pitfalls

PitfallReal ConsequenceSolution
Flow sensitivityImprecisionUse flow-sensitive
InterproceduralMissing callsBuild call graph
ImprecisionToo many aliasesFlow sensitivity

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.