inferMutationAliasingRanges

File

src/Inference/InferMutationAliasingRanges.ts

Purpose

This pass builds an abstract model of the heap and interprets the effects of the given function to determine: (1) the mutable ranges of all identifiers, (2) the externally-visible effects of the function (mutations of params/context-vars, aliasing relationships), and (3) the legacy Effect annotation for each Place.

Input Invariants

Output Guarantees

Algorithm

The pass operates in three main phases:

Part 1: Build Data Flow Graph and Infer Mutable Ranges

  1. Creates an AliasingState which maintains a Node for each identifier
  2. Iterates through all blocks and instructions, processing effects in program order
  3. For each effect:
    • Create/CreateFunction: Creates a new node in the graph
    • CreateFrom/Assign/Alias: Adds alias edges between nodes (with ordering index)
    • MaybeAlias: Adds conditional alias edges
    • Capture: Adds capture edges (for transitive mutations)
    • Mutate*: Queues mutations for later processing
    • Render: Queues render effects for later processing
  4. Phi node operands are connected once their predecessor blocks have been visited
  5. After the graph is built, mutations are processed:
    • Mutations propagate both forward (via edges) and backward (via aliases/captures)
    • Each mutation extends the mutableRange.end of affected identifiers
    • Transitive mutations also traverse capture edges backward
    • MaybeAlias edges downgrade mutations to Conditional
  6. Render effects are processed to mark values as rendered

Part 2: Populate Legacy Per-Place Effects

Part 3: Infer Externally-Visible Function Effects

Key Data Structures

AliasingState

The main state class maintaining the data flow graph:

Node

Represents an identifier in the data flow graph:

type Node = {
  id: Identifier;
  createdFrom: Map<Identifier, number>;   // CreateFrom edges (source -> index)
  captures: Map<Identifier, number>;       // Capture edges (source -> index)
  aliases: Map<Identifier, number>;        // Alias/Assign edges (source -> index)
  maybeAliases: Map<Identifier, number>;   // MaybeAlias edges (source -> index)
  edges: Array<{index, node, kind}>;       // Forward edges to other nodes
  transitive: {kind: MutationKind; loc} | null;  // Transitive mutation info
  local: {kind: MutationKind; loc} | null;       // Local mutation info
  lastMutated: number;                     // Index of last mutation affecting this node
  mutationReason: MutationReason | null;   // Reason for mutation
  value: {kind: 'Object'} | {kind: 'Phi'} | {kind: 'Function'; function: HIRFunction};
  render: Place | null;                    // Render context if used in JSX
};

MutationKind

Enum describing mutation certainty:

enum MutationKind {
  None = 0,
  Conditional = 1,  // May mutate (e.g., via MaybeAlias or MutateConditionally)
  Definite = 2,     // Definitely mutates
}

Edge Cases

Phi Nodes

Transitive vs Local Mutations

MaybeAlias

Function Values

Render Effect Propagation

TODOs

  1. Assign effects should have an invariant that the node is not initialized yet. Currently InferFunctionExpressionAliasingEffectSignatures infers Assign effects that should be Alias, causing reinitialization.

  2. Phi place effects are not properly set today.

  3. Phi mutable range start calculation is imprecise - currently just sets it to the instruction before the block rather than computing the exact start.

Example

Consider the following code:

function foo() {
  let a = {};   // Create a (instruction 1)
  let b = {};   // Create b (instruction 3)
  a = b;        // Assign a <- b (instruction 8)
  mutate(a, b); // MutateTransitiveConditionally a, b (instruction 16)
  return a;
}

The pass builds a graph:

  1. Creates node for {} at instruction 1 (initially assigned to a)
  2. Creates node for {} at instruction 3 (initially assigned to b)
  3. At instruction 8, creates alias edge: b -> a with index 8
  4. At instruction 16, mutations are queued for a and b

When processing the mutation of a at instruction 16:

The output shows identifiers with range annotations like $25[3:17] meaning:

For aliased values, the ranges are unified - all values that could be affected by a mutation have their ranges extended to include that mutation point.