Skip to content

Chapter 19 · Memory: Alias Analysis & Memory Optimizations

Part 4 · Optimization · about 3 weeks · Previous: Ch 18 · Next: Ch 20

The problem

After mem2reg (Ch 16) everything whose address is taken — arrays, structs, heap objects, globals, locals passed by pointer — stays in memory, reached through load, store, getelementptr and calls. The input of this chapter is such a program (LLVM IR, or the small pointer language of Lessons 19.4–19.8); the output is twofold. First, alias information: for two memory accesses, can they touch the same bytes (NoAlias, MayAlias, PartialAlias, MustAlias)? For an instruction and a location, may it read or write it (mod/ref)? For a pointer, which allocation sites may it point to (points-to sets)? Second, memory optimizations that use it: scalar replacement of aggregates, dead-store elimination, store-to-load forwarding and load PRE, MemCpyOpt and scalar promotion, organised around Memory SSA's def-use chains for memory. Exact aliasing is undecidable (Theorem 19.1.8), so every analysis approximates, always towards "may alias". In LLVM 23 the analyses are basic-aa, tbaa, scoped-noalias-aa, globals-aa and MemorySSA, and the transformations sroa, dse, gvn, memcpyopt and licm; in pebblec your pebble-dse and pebble-loadfwd run on them, and your comparison lab implements the whole-program points-to analyses LLVM does not ship.

What you will be able to do

  • Answer alias and mod/ref queries on concrete IR by BasicAA's rules (objects, offsets, sizes, capture), TBAA's struct paths and scoped noalias tags, and predict what an AA pipeline returns.
  • Solve Andersen's inclusion constraints by hand (worklist, every step), identify the cycles a detector collapses, and prove the cubic bound and the soundness theorem.
  • Run Steensgaard's unification and Das's one-level flow by hand, and prove Andersen ⊆ Das ⊆ Steensgaard.
  • Explain what flow, field and context sensitivity (call strings, cloning, summaries, object and type sensitivity) buy and cost, and read a Datalog formulation of each.
  • Build Memory SSA for a function by hand — numbering, phis at the iterated dominance frontier, renaming, optimized uses — and check it against opt -passes='print<memoryssa>'.
  • Implement pebble-dse and pebble-loadfwd on LLVM's AAResults and MemorySSA, and Andersen and Steensgaard over LLVM IR; check soundness at run time and measure precision and speed.
  • Find where LLVM 23, GCC 14, Go and HotSpot implement each technique, and read the relevant code.

Prerequisites: Ch 9 (the memory model, GEP, noalias, TBAA metadata), Ch 14 (lattices and least fixed points, worklists, Datalog in Lesson 14.8), Ch 15 (dominators, iterated dominance frontiers, SCCs), Ch 16 (SSA construction, mem2reg, Memory SSA in Lesson 16.8), Ch 17 (GVN, PRE, DSE in Lesson 17.3). Ch 18's LICM is referenced for scalar promotion.

Notation

Shared notation follows the house notation: §1 (sets, functions, logic), §2 (orders and lattices), §3 (graphs), §4 (dominance, \(\mathrm{DF}^{+}\)), §7 (dataflow, SSA). Orientation: points-to sets grow up the lattice (\(\subseteq\)), and \(\mathrm{pts}^*\) is a least solution; the precision order of alias answers puts MayAlias at the bottom. In this chapter:

Symbol Meaning
\(L = (p, s)\), \(\llbracket L \rrbracket\) a memory location (pointer, size); the byte interval it denotes (Definition 19.1.1)
\(\mathsf{No}\), \(\mathsf{May}\), \(\mathsf{Partial}\), \(\mathsf{Must}\) the four alias answers (Definition 19.1.2)
\(\mathit{MR}(I, L)\) mod/ref information: a subset of \(\{\mathsf{ref}, \mathsf{mod}\}\) (Definition 19.1.4)
\(\mathrm{obj}(p)\) the underlying object of a pointer (Definition 19.2.1)
\((B, A, o)\) a TBAA access tag: base type, access type, offset (Definition 19.3.2)
\(\mathrm{AS}(I)\), \(\mathrm{NA}(I)\) !alias.scope and !noalias scope lists (Definition 19.3.5)
\(V\), \(\mathit{Obj}\) variables of a pointer program; address-taken variables (objects) (Definition 19.4.1)
\(\mathrm{pts}(v)\), \(\mathrm{pts}^*\) a points-to set; the least solution of the inclusion constraints (Definition 19.4.2)
\(p = \&a\), \(p = q\), \(p = {*}q\), \({*}p = q\) the four statement forms (Definition 19.4.1)
\(\mathrm{find}(x)\), \(\mathrm{ptr}(C)\) union-find representative; the class that class \(C\) points to (Definitions 19.5.1–19.5.2)
\(\mathrm{val}(v)\), \(D(n)\) Das's value node and dereference node (Definition 19.5.4)
\(\alpha(m, n)\) the inverse Ackermann function (Definition 19.5.1)
\(G^{\mathrm{in}}_\ell\), \(G^{\mathrm{out}}_\ell\) flow-sensitive points-to graphs at a program point (Definition 19.6.1)
\(o.k\) field (offset) \(k\) of object \(o\) (Definition 19.6.4)
\(\mathit{Ctx}\), \(\mathrm{push}\), \(\pi\) contexts, the context of a callee, projection (Definition 19.7.1)
\(\mathsf{NoEscape} \sqsubset \mathsf{ArgEscape} \sqsubset \mathsf{GlobalEscape}\) escape states (Definition 19.9.1)
\(x \mapsto y\), \(\mathrm{lseg}(x, y)\), \(\ast\) separation-logic points-to, list segment, separating conjunction (Definition 19.9.4)
MemoryDef, MemoryUse, MemoryPhi, liveOnEntry Memory SSA accesses; phi@B is the MemoryPhi of block B (Definition 19.10.1)
\(n\), \(m\), \(k\), \(a\) numbers of variables (or blocks), statements, objects, memory accesses — each complexity section restates its variables

Numbered statements are N.k.m (chapter, lesson, counter), as in NOTATION.md §9.

Technique map

Family Techniques (origin) Lesson
Alias queries and the AA interface may/must/partial/no alias queries over memory locations and mod/ref queries (the vocabulary surveyed by Hind 2001; LLVM AAResults); chaining analyses into a pipeline (LLVM AAManager, GCC's alias oracle) 19.1
Local alias rules BasicAA-style reasoning: distinct allocations, GEP decomposition and offset arithmetic, object sizes, phi/select (LLVM); capture/escape tracking (LLVM CaptureTracking); GlobalsAA (mod/ref of non-address-taken globals) 19.2
Language rules Type-based alias analysis (Diwan, McKinley & Moss 1998; C/C++ strict aliasing with the char/std::byte exemption; LLVM struct-path TBAA); scoped noalias (C99/C11 restrict, LLVM noalias and scoped metadata created by the inliner) 19.3
Inclusion-based points-to Andersen's analysis (Andersen 1994); online cycle detection (Pearce, Kelly & Hankin 2003), lazy and hybrid cycle detection (Hardekopf & Lin 2007), offline variable substitution (Rountev & Chandra 2000; Hardekopf & Lin 2007); wave and deep propagation (Pereira & Berlin 2009) 19.4
Unification-based points-to Steensgaard's analysis (Steensgaard 1996; union-find by Tarjan 1975); Das's one-level flow (Das 2000) 19.5
Flow and field sensitivity Dense and staged sparse flow-sensitive analysis, SFS (Hardekopf & Lin 2011); field-insensitive, field-based and field-sensitive models, Pearce's offset constraints (Pearce, Kelly & Hankin 2007) 19.6
Context sensitivity Call strings and k-CFA (Sharir & Pnueli 1981; Shivers 1988); cloning (Whaley & Lam 2004) and summaries (Sharir & Pnueli's functional approach; LLVM function attributes); object sensitivity (Milanova, Rountev & Ryder 2005) and type sensitivity (Smaragdakis, Bravenboer & Lhoták 2011) 19.7
Declarative and demand-driven analysis Points-to in Datalog (Doop: Bravenboer & Smaragdakis 2009; Soufflé: Jordan, Scholz & Subotić 2016; BDDs in bddbddb: Whaley & Lam 2004, Whaley et al. 2005); demand-driven analysis (Heintze & Tardieu 2001; refinement-based CFL reachability, Sridharan & Bodík 2006) 19.8
Escape and shape analysis Escape analysis (Choi et al. 1999; Go, HotSpot C2); shape analysis overview (three-valued logic and TVLA: Sagiv, Reps & Wilhelm 2002; separation logic: Reynolds 2002; bi-abduction in Infer: Calcagno et al. 2009) 19.9
Memory SSA MemoryDef/Use/Phi, clobber walkers, optimized uses, cost limits (Novillo 2007; LLVM MemorySSA) 19.10
Memory optimizations SROA (LLVM); MemorySSA-based dead-store elimination (LLVM dse); store-to-load forwarding and load PRE (LLVM GVN); MemCpyOpt (LLVM); LICM scalar promotion (LLVM LICM, Ch 18) 19.11
flowchart LR
  Q[Alias and mod/ref queries] --> BA[BasicAA + capture]
  Q --> TB[TBAA / scoped noalias]
  Q --> GA[GlobalsAA]
  A94[Andersen 1994<br/>inclusion] -->|cycle collapse| CD[Online / lazy / hybrid<br/>cycle detection]
  A94 -->|rounds in topological order| WV[Wave / deep propagation]
  A94 -->|equalities instead of inclusions| ST[Steensgaard 1996<br/>unification]
  ST -->|inclusion at the top level| DAS[Das 2000<br/>one-level flow]
  A94 -->|per program point, staged| SFS[SFS 2011]
  A94 -->|per field / offset| FS[Field sensitivity]
  A94 -->|per context| CS[Call strings / cloning /<br/>object and type sensitivity]
  A94 -->|rules| DL[Datalog: Doop, Soufflé, bddbddb]
  A94 -->|per query| DD[Demand-driven]
  A94 -->|reachability from roots| EA[Escape analysis]
  EA -->|heap structure| SH[Shape analysis]
  BA --> MSSA[Memory SSA + walker]
  TB --> MSSA
  MSSA --> OPT[SROA · DSE · forwarding/PRE ·<br/>MemCpyOpt · LICM promotion]

Who uses what

System Technique Notes
LLVM 23 BasicAA, scoped-noalias, TBAA, GlobalsAA in the AA pipeline; capture tracking; function-attrs summaries; MemorySSA; SROA, DSE, GVN (forwarding, load PRE), MemCpyOpt, LICM promotion; no whole-program points-to (CFL-Steens/Anders removed after LLVM 15) lessons 19.1–19.3, 19.7, 19.10–19.11
GCC 14 Andersen-style, field-sensitive points-to (tree-ssa-structalias.cc: offline variable substitution, HCD, LCD) under an alias oracle (tree-ssa-alias.cc) with TBAA alias sets; .MEM virtual operands; SRA, DSE, FRE/PRE 19.4, 19.6
Go 1.24 escape analysis deciding stack vs heap for every allocation, with per-parameter summaries 19.9
HotSpot (JDK 21) C2 escape analysis on connection graphs: scalar replacement, lock elimination 19.9
SVF 3.3 Andersen (wave + difference propagation), Steensgaard, staged flow-sensitive, demand-driven context-sensitive analyses over LLVM IR 19.4, 19.6, 19.8
Doop / Soufflé Java points-to in Datalog with call-site, object and type sensitivity 19.7, 19.8
CIL Steensgaard, one-level flow and Golf points-to analyses for C 19.5
Facebook Infer separation-logic shape analysis with bi-abduction 19.9
Pebble pebble-dse, pebble-loadfwd; the lab's Andersen (+★ cycle elimination) and Steensgaard with run-time soundness checks exercises, lab

Comparison

The fixed columns follow docs/authoring/DEPTH_CONTRACT.md §3 item 8; each lesson repeats its own rows and defines its variables.

Memory and alias queries: may, must, partial and no alias; mod/ref; AA pipelines (lesson 19.1)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Alias queries Four answers; PartialAlias/MustAlias need offsets and sizes; soundness only in the "may" direction per query \(O(d \cdot g)\) for local AAs · microseconds, cached per pass aa-eval prints every pair; answers explain nothing about why Low for the interface, high for good members every memory transform (DSE, GVN, LICM, vectorizer)
Mod/ref queries Four-point lattice per instruction; calls limited by memory effects and escape facts \(O(r \cdot q)\) per call query aa-eval -print-all-alias-modref-info Medium (memory-effect attributes, capture tracking) MemorySSA construction, DSE, LICM around calls
AA pipelines At least as precise as each member (Proposition 19.1.9); inherits any member's unsoundness \(\le k\) member queries, early exit the answer, not the member that gave it Low (a list); ordering matters for speed LLVM AAManager, -aa-pipeline; GCC's single oracle

Local alias rules: BasicAA, capture tracking, GlobalsAA (lesson 19.2)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
BasicAA Local facts only: distinct allocations, offsets, sizes, phi/select; nothing about what arguments or loaded pointers point to \(O(D \cdot g)\) per query, cached · the cheapest AA Four answers with offsets (PartialAlias) Medium (many rules, many corner cases) First member of every LLVM AA pipeline
Capture tracking Decides "private local" exactly for the use patterns it knows; conservative on any other use \(O(\min(u, 100))\) per pointer yes/no (plus capture components) Low–medium BasicAA escape sources, DSE at function exit, call mod/ref, captures(none) inference
GlobalsAA Mod/ref of calls for internal non-address-taken globals; gives up on indirect calls and callbacks \(O(I + C)\) once per module per-function mod/ref summaries Medium (call graph, SCC order) LLVM -O2 module pass; GCC ipa-reference

Language rules as alias facts: type-based alias analysis and scoped noalias (lesson 19.3)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
TBAA Separates accesses of unrelated types and different struct paths; nothing for same-type accesses through different pointers; unsound for programs that type-pun \(O(h + w)\) per query · very cheap May/No per pair of tags; tags are readable in the IR Front end: map the language's types to a DAG; back end: ~700 lines C/C++ at -O1+; disabled by -fno-strict-aliasing (Linux kernel, many embedded code bases)
Scoped noalias Separates accesses based on different restrict/noalias pointers, also after inlining; exactly as good as the programmer's promises \(O(\lvert \mathrm{AS} \rvert + \lvert \mathrm{NA} \rvert)\) per query No/May; scopes are named after the parameter Inliner translation plus a ~200-line AA Numerical kernels (restrict), Rust &mut, Fortran dummy arguments

Inclusion-based points-to analysis: Andersen, cycle detection, wave and deep propagation (lesson 19.4)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Andersen's analysis The most precise flow- and context-insensitive analysis for the model (least solution); on the lab corpus 45 of 224 alias pairs MayAlias, vs 51 for Steensgaard \(O(n^3)\) with difference propagation · 75 s on the lab's 4000-variable stress input without cycle detection One set per pointer; exact least solution, easy to inspect Low for the basic solver (~200 lines), medium with all optimizations GCC's points-to; SVF; static analyzers; the lab
Cycle detection (online, lazy, hybrid) Same sets (Theorem 19.4.14) same bound · 40× faster than plain on the lab's stress run (1.8 s vs 75 s) Same sets; collapsed-node counts Medium (union-find, Tarjan, triggered edges) GCC (HCD + LCD), SVF, research solvers
Wave / deep propagation Same sets (Theorem 19.4.15) few rounds of linear work on typical graphs; worst case as above Same sets Medium (SCCs and topological order per round) SVF (AndersenWaveDiff), research

Unification-based points-to analysis: Steensgaard and Das's one-level flow (lesson 19.5)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Steensgaard's analysis Coarsest of the three: ⊇ Andersen (Corollary 19.5.7); 51 vs 45 MayAlias pairs of 224 on the lab corpus, 28 vs 15 of 36 on the running example \(O((n + m)\,\alpha)\) · 0.3 s on the 4000-variable stress input (Andersen: 75 s, 1.8 s with cycles) One class per pointee: a storage-shape graph; sets are shared, so large Low (union-find, ~150 lines) Very large code bases, first pass of demand-driven or staged analyses, CIL, historic LLVM CFL-AA
Das's one-level flow Between the two (Theorem 19.5.9); equal to Andersen on the running example's top-level u near-linear · close to Steensgaard [Das00] Flow graph + classes Medium (flow edges plus unification) Large C programs where Steensgaard is too coarse (Das's MSR tools), CIL's olf

Flow sensitivity and field sensitivity (lesson 19.6)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Flow-sensitive points-to (SFS) Per-point facts with strong updates; ⊆ Andersen (Theorem 19.6.6); SFS = dense (Theorem 19.6.7) dense: infeasible at scale · SFS: auxiliary + sparse, millions of lines [HL11] Sets per SSA variable and per memory version High (memory SSA per object, strong-update rules) SVF, bug finders (null dereference, use-after-free), security analyses
Field sensitivity field-sensitive ⊆ field-insensitive (Theorem 19.6.8); field-based incomparable, unsound for C locations × fields · slower than field-insensitive [PKH07] Sets per field (s.0+64) Medium (offsets, PWC collapse) GCC's points-to, SVF, Java analyses (field-based or field-sensitive)

Context sensitivity: call strings, cloning and summaries, object and type sensitivity (lesson 19.7)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Call-string sensitivity (k-CFA) Separates calls by the last \(k\) call sites; wrappers deeper than \(k\) merge \(S^k\) contexts · 1–2-CFA feasible Facts per (context, variable) Low on top of a solver (a push function) Functional-language CFA, C analyzers, Doop's call-site variants
Cloning and summaries Cloning: exact for non-recursive programs; summaries: the same with one analysis per function (Theorem 19.7.9) cloning exponential (BDDs); summaries linear to exponential in summary size Per-path facts, or per-function summaries High (BDDs, or symbolic summaries) bddbddb; LLVM function-attrs and Go parameter tags (coarse summaries)
Object and type sensitivity Object: the most useful for OO code; type: coarser (Theorem 19.7.10) \(H^k\) / \(T^k\) contexts · 2-object+1-heap practical; type much cheaper Facts per receiver object or class Medium (heap contexts) Java analyses (Doop, WALA, Soot)

Points-to analysis as Datalog, and on demand (lesson 19.8)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Datalog points-to Whatever the rules say: Andersen (Theorem 19.8.5) or context/field-sensitive variants \(O(n^3)\) derivations · Soufflé is competitive with hand-written solvers [BS09, JSS16] Relations you can query; every fact has a derivation (provenance) Very low for the analysis (a page of rules), high for the engine Doop, CodeQL, research; the Lab 14 engine
Demand-driven points-to Exact per query (Theorem 19.8.6), or budget-limited proportional to the relevant part · fast for few queries Answers per query; "out of budget" fallback Medium–high (caching, budgets) IDE analyses, bug finders, SVF DDA, JIT-time queries

Escape analysis and shape analysis (lesson 19.9)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
Escape analysis Three states per allocation site; flow-insensitive per method (partial EA adds path sensitivity) near-linear per method · runs on every Go build and every C2 compilation Per-site decisions ("moved to heap: q"); explains allocations Medium Go (stack vs heap), HotSpot C2 and Graal (scalar replacement, lock elision)
Shape analysis Proves list/tree shapes, acyclicity, disjointness; far beyond points-to exponential · seconds to minutes per procedure Symbolic heaps or 3-valued structures; can prove memory safety High Verifiers and bug finders (Infer, TVLA-based research tools)

Memory SSA in depth: accesses, clobber walkers, optimized uses and cost limits (lesson 19.10)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
MemorySSA Def-use chains for all memory; precision comes from the walker's alias queries (one variable, refined on demand); bounded by \(K\) construction linear · queries capped at 100 steps print<memoryssa>, print<memoryssa-walker>; readable versions High (~2,700 lines plus the updater); for clients, low LLVM DSE, LICM, EarlyCSE, NewGVN; GCC's .MEM virtual operands

Memory optimizations: SROA, dead-store elimination, forwarding and load PRE, MemCpyOpt, LICM promotion (lesson 19.11)

Technique Power / precision Speed (asymptotic · practical) Output / error quality Implementation effort Typical use
SROA Removes whole aggregates with constant-offset uses; blocked by escaping or variable-offset uses \(O(s \log s)\) per alloca · runs several times per pipeline Promoted SSA values; readable IR High (~6,400 lines in SROA.cpp: slicing, rewriting, types) LLVM SROA, GCC SRA; essential for C++ abstraction removal
Dead-store elimination Complete and partial overwrites, stores dead at exit; limited by mod/ref of calls per store \(O(\min(K, a))\) · capped scans Deleted or shortened stores High in LLVM, medium for E1 LLVM dse (function simplification pipeline), GCC dse
Store-to-load forwarding and load PRE Forwarding needs a dominating available value; PRE handles one missing predecessor per load one clobber query (+ PRE per predecessor) Replaced loads, inserted .pre loads and phis Medium (E2), high (GVN) LLVM GVN and EarlyCSE; GCC FRE/PRE
MemCpyOpt Pattern-based: memset formation, copy forwarding, call-slot linear with MemorySSA queries Fewer, larger intrinsics Medium–high LLVM memcpyopt (after SROA and before DSE)
LICM scalar promotion Whole loops' accesses to one must-alias location; full or load-only alias sets per loop, capped Preheader load, exit stores, header phis Medium (SSA updater, safety) LLVM LICM, GCC tree-ssa-loop-im.cc

Comparison-lab results (reproduce with build/<preset>/bin/ch19-pointsto --table labs/ch19-points-to/corpus/*.ll and --stress N on a solution build): on the five corpus programs (59 query pointers, 224 pairs), Andersen answers MayAlias for 45 pairs and Steensgaard for 51; Andersen with lazy cycle detection gives identical sets and collapses 13 nodes. On random programs with 4000 variables and 12 000 statements, Andersen takes about 75 s, Andersen with lazy cycle detection 1.8 s (10 269 nodes collapsed) and Steensgaard 0.3 s.

Route through this chapter

Step What Techniques How it is exercised
1 Lesson 19.1 alias and mod/ref queries, AA pipelines quiz; flashcards
2 Lesson 19.2 BasicAA, capture tracking, GlobalsAA quiz; gep-offset drill (Ch 9)
3 Lesson 19.3 TBAA, scoped noalias / restrict drill tbaa-query
4 Lesson 19.4 Andersen, cycle detection, wave propagation drill points-to-andersen; lab
5 Lesson 19.5 Steensgaard, Das drill points-to-steensgaard; lab
6 Lesson 19.6 SFS, field sensitivity quiz; lab ★
7 Lesson 19.7 call strings, cloning and summaries, object/type sensitivity quiz; Soufflé boxes
8 Lesson 19.8 Datalog, demand-driven quiz; your Lab 14 Datalog engine
9 Lesson 19.9 escape analysis, shape analysis quiz; flashcards
10 Lesson 19.10 Memory SSA drill memoryssa-build
11 Lesson 19.11 SROA, DSE, forwarding/PRE, MemCpyOpt, LICM promotion quiz; exercises E1–E2
12 Exercises E1–E2 Pebble implements pebble-dse and pebble-loadfwd on MemorySSA ./course test 19
13 Comparison lab labs/ch19-points-to/ Andersen (+★ cycle elimination) vs Steensgaard over LLVM IR ch19.lab, ch19.PointsTo.*; ch19-pointsto --table
14 Theory test all ./course quiz 19 (≥ 80 % to finish)

Practice and check

./course drill points-to-andersen --difficulty easy   # warm up; --solution shows every worklist step
./course drill points-to-steensgaard                  # joins, classes, what unification loses
./course drill memoryssa-build                         # defs, phis, defining accesses, clobbers
./course drill tbaa-query                              # struct-path TBAA on C accesses
./course flash 19                                      # daily, a few minutes
./course quiz 19                                       # after the lessons
./course test 19                                       # after the exercises and the lab
./course status                                        # done = quiz ≥ 80 % and tests pass

References

The chapter's annotated bibliography (papers, textbook sections, standards, pinned source files and documentation) is in references.md. Start with: [Hin01] (the map of the design space), [And94] and [Ste96] (the two classic analyses), [HL07] (making Andersen fast), [DMM98] and [C11] (type-based rules and restrict), [LLVM-AADoc] and [LLVM-MemorySSADoc] (the interfaces your passes use). [Dragon2] and [SPA] are the textbook companions.