---
generated: true
model: claude-opus-4.8
generated_at: 2026-06-29T18:50:39Z
source_commit: c21ead2690b5b9fa4a582f6b51a4cd5fb34d29d8
watched_paths_digest: e27926ca78614bca20d3b57a5268d5884f642e04074ed66afbbed157eadbfdd7
warning: "Auto-generated. May drift from source. Do not edit by hand."
---

# Differentiation

This page is the per-opcode reference for the Slang IR opcodes used
by the automatic-differentiation passes: opcodes that construct and
project differential pairs, opcodes that turn a function value into
its forward or reverse-mode derivative, opcodes that thread the
reverse-mode primal context through the call graph, and opcodes that
support checkpointing and rematerialization.

The intended reader is a compiler engineer working on the autodiff
passes (`slang-ir-autodiff*.cpp`) or on emit paths that need to
understand a partially-differentiated IR.

## Source

The differential-pair construction / projection opcodes live in
their own three Lua intermediate groups around lines 930-958 of
[slang-ir-insts.lua](../../../../source/slang/slang-ir-insts.lua):
`MakeDifferentialPairBase`, `DifferentialPairGetDifferentialBase`,
and `DifferentialPairGetPrimalBase`. The bulk of the
differentiation-operator opcodes (`ForwardDifferentiate`,
`BackwardDifferentiate`, the propagate / primal / remat variants,
and the legacy-bridge opcodes) live under the `TranslateBase`
hoistable group at line ~2642. Three additional opcodes that are
specific to checkpointing — `checkpointObj`,
`ReportCheckpointStore`, and `loopExitValue` — live in the
value-producing section (around lines ~1512-1519). A handful of
related opcodes are documented in sibling pages: the differential
pair *types* are in [types.md](types.md); generic-annotation opcodes
that mark differentiable types (`DifferentiableTypeAnnotation`,
`DifferentiableTypeDictionaryItem`) are in [misc.md](misc.md).

Two further opcodes that identify the built-in `IDifferentiable`
family of interface requirements live just after the ordinary
`StructKey` entry: `BuiltinRequirementKey` (a hoistable inst, line
~841) and the `BuiltinRequirementDecoration` (line ~2133). These are
produced during AST lowering, not by the autodiff passes, but the
autodiff passes are their primary consumer; they are described under
[built-in requirement keys](#built-in-requirement-keys) below.

C++ wrappers are declared in
[slang-ir-insts.h](../../../../source/slang/slang-ir-insts.h). The
opcodes are introduced primarily by the autodiff IR passes
(`slang-ir-autodiff-fwd.cpp`, `slang-ir-autodiff-rev.cpp`,
`slang-ir-autodiff-unzip.cpp`,
`slang-ir-autodiff-transcribe.cpp`, and friends); a small number
are produced by
[slang-lower-to-ir.cpp](../../../../source/slang/slang-lower-to-ir.cpp)
when lowering the user-facing `__fwd_diff` syntactic form
(`visitForwardDifferentiateExpr` emits `ForwardDifferentiate`), when
lowering the differentiation `Val`s stored in witness tables
(`visitBackwardDifferentiateVal` emits `BackwardDifferentiate`), or
when assigning requirement keys to the built-in differentiable
interfaces. The `__bwd_diff` expression form
(`BackwardDifferentiateExpr`) is resolved before IR lowering, so its
`visit*` calls `SLANG_UNEXPECTED` rather than emitting an opcode.

## Family hierarchy

```mermaid
flowchart TD
  IRInst --> Diff[Differentiation]
  Diff --> MakeDifferentialPairBase
  Diff --> DifferentialPairGetDifferentialBase
  Diff --> DifferentialPairGetPrimalBase
  Diff --> TranslateBase
  Diff --> BuiltinRequirementKey
  Diff --> Checkpointing
  MakeDifferentialPairBase --> MakeDiffPair
  MakeDifferentialPairBase --> MakeDiffRefPair
  DifferentialPairGetDifferentialBase --> GetDifferential
  DifferentialPairGetDifferentialBase --> GetDifferentialPtr
  DifferentialPairGetPrimalBase --> GetPrimal
  DifferentialPairGetPrimalBase --> GetPrimalRef
  TranslateBase --> FwdOps["Forward-mode ops"]
  TranslateBase --> RevOps["Reverse-mode ops"]
  TranslateBase --> LegacyBridge["Legacy bridge ops"]
  TranslateBase --> WitnessSyn["Synthesized derivative witnesses"]
  Checkpointing --> checkpointObj
  Checkpointing --> ReportCheckpointStore
  Checkpointing --> loopExitValue
  Checkpointing --> detachDerivative
```

## Opcodes

### Differential-pair construction

`MakeDifferentialPairBase` groups the two opcodes that bundle a
primal value and its differential into a single pair value. The
opcodes have matching `*Ref` siblings for pair-of-pointers — used
when both halves of the pair need to remain addressable.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `MakeDiffPair` | `MakeDifferentialPair` | `primal, differential` | | (synthesized) | Pairs a value-typed primal with its differential. |
| `MakeDiffRefPair` | `MakeDifferentialPtrPair` | `primal, differential` | | (synthesized) | Pairs a pointer-typed primal with its differential pointer. |

### Differential-pair projection

`DifferentialPairGetDifferentialBase` projects the differential
half; `DifferentialPairGetPrimalBase` projects the primal half. The
`*Ptr` / `*Ref` variants accept pair-of-pointer values.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `GetDifferential` | `DifferentialPairGetDifferential` | `pair` | | (synthesized) | Reads the differential component of a `DiffPair`. |
| `GetDifferentialPtr` | `DifferentialPtrPairGetDifferential` | (variadic, `min=1`) | | (synthesized) | Reads the differential pointer of a `DiffRefPair`. |
| `GetPrimal` | `DifferentialPairGetPrimal` | `pair` | | (synthesized) | Reads the primal component of a `DiffPair`. |
| `GetPrimalRef` | `DifferentialPtrPairGetPrimal` | `ptrPair` | | (synthesized) | Reads the primal pointer of a `DiffRefPair`. |

### Differentiation operators

The `TranslateBase` group holds every opcode that *translates* a
function value (or witness table) into a differentiated form. All
are hoistable so identical translation requests dedupe to one IR
value.

#### Forward-mode

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `ForwardDifferentiate` | — | `baseFn` | H | `ForwardDifferentiateExpr` (`__fwd_diff(...)`) in `slang-lower-to-ir.cpp` | Translates a function value into its forward-mode derivative. |
| `ForwardDifferentiatePropagate` | — | (variadic, `min=1`) | H | (synthesized) | Forward-mode propagate variant produced during the unzip / transcribe pipeline. |
| `TrivialForwardDifferentiate` | — | (variadic, `min=1`) | H | (synthesized) | Forward derivative of a function known to be a no-op on derivatives (e.g. a non-differentiable helper). |

#### Reverse-mode

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `BackwardDifferentiate` | — | (variadic, `min=3`) | H | (synthesized) | Translates a function value into its reverse-mode adjoint. |
| `BackwardDifferentiatePrimal` | — | (variadic, `min=1`) | H | (synthesized) | Primal-only side of the reverse-mode pair, used to compute the values reverse mode needs to remember. |
| `BackwardDifferentiatePropagate` | — | (variadic, `min=1`) | H | (synthesized) | Backwards-propagation side of the reverse-mode pair; produces the adjoint update. |
| `BackwardRemat` | — | (variadic, `min=1`) | H | (synthesized) | Rematerialization variant: recomputes primal values rather than reading them from a checkpoint. |
| `TrivialBackwardDifferentiate` | — | (variadic, `min=1`) | H | (synthesized) | Trivial-adjoint counterpart of `TrivialForwardDifferentiate`. |
| `TrivialBackwardDifferentiatePrimal` | — | (variadic, `min=1`) | H | (synthesized) | Trivial primal side. |
| `TrivialBackwardDifferentiatePropagate` | — | (variadic, `min=1`) | H | (synthesized) | Trivial propagate side. |
| `TrivialBackwardRemat` | — | (variadic, `min=1`) | H | (synthesized) | Trivial remat side. |

#### Legacy bridge

These opcodes exist solely to map the historical "single function
bundles primal + propagate" representation onto the current
unzipped form. New code should not produce them; they are consumed
by the legacy-bridge pass and replaced with their non-legacy
counterparts.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `LegacyBackwardDifferentiate` | — | (variadic, `min=3`) | H | (synthesized) | Old-style reverse-mode opcode kept for backward-compat IR loads. |
| `BackwardFromLegacyBwdDiffFunc` | — | (variadic, `min=2`) | H | (synthesized) | Wraps a legacy combined reverse function as the current form. |
| `BackwardPrimalFromLegacyBwdDiffFunc` | — | (variadic, `min=2`) | H | (synthesized) | Extracts the primal half from a legacy combined function. |
| `BackwardRematFromLegacyBwdDiffFunc` | — | (variadic, `min=2`) | H | (synthesized) | Extracts the remat half. |
| `BackwardPropagateFromLegacyBwdDiffFunc` | — | (variadic, `min=2`) | H | (synthesized) | Extracts the propagate half. |

#### Synthesized derivative witnesses

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `FunctionCopy` | — | (variadic, `min=1`) | H | (synthesized) | Produces a cloned copy of a function used as the seed for a synthesized derivative. |
| `SynthesizedForwardDerivativeWitnessTable` | — | (variadic, `min=1`) | H | (synthesized) | Witness table mapping `IDifferentiable.dAdd` etc. onto a synthesized forward derivative. |
| `SynthesizedBackwardDerivativeWitnessTable` | — | (variadic, `min=1`) | H | (synthesized) | Witness table for a synthesized reverse-mode derivative. |
| `MakeIDifferentiableWitness` | — | (variadic, `min=1`) | H | (synthesized) | Builds an `IDifferentiable` witness for a type that does not declare one. |
| `SynthesizedBackwardDerivativeWitnessTableFromLegacyBwdDiffFunc` | — | (variadic, `min=2`) | H | (synthesized) | Bridges legacy combined reverse functions into the modern witness form. |
| `IdentityRemat` | — | (variadic, `min=1`) | H | (synthesized) | Identity remat marker used by user-provided `__func_extension __apply` when `MinimalContext == BwdCallable`, so the backward-pass machinery treats the user's apply result as the recomputable context. |

### Autodiff temporaries

Placeholders created and consumed by the reverse-mode splitting and
backward auto-diff passes; none survive past those passes.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `LoadReverseGradient` | — | `value` | | (synthesized) | Placeholder for the currently accumulated derivative to pass as a nested-call `dOut` argument. |
| `ReverseGradientDiffPairRef` | — | `primal, diff` | | (synthesized) | Placeholder pair carrying the primal and accumulated derivative for an inout argument in a nested call. |
| `PrimalParamRef` | — | `referencedParam` | | (synthesized) | Reference to an inout parameter for use in the primal portion of the split function. |
| `DiffParamRef` | — | `referencedParam` | | (synthesized) | Reference to an inout parameter for use in the back-prop portion of the split function. |

### Differential type info

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `DiffTypeInfo` | — | — | H | (synthesized) | Holds witness tables for differential type info (`thisType` witness, return-type witness, parameter witnesses); lowered to a `MakeTuple` after specialization. |

### Built-in requirement keys

The `IDifferentiable` / `IBackwardDifferentiable` / `IBwdCallable`
interfaces are recognized by the compiler, and the autodiff passes
need to look their requirements up by *role*
(`BuiltinRequirementKind::DifferentialType`, `DAddFunc`,
`DifferentialWitness`, etc.) rather than by entry position, which is
not semantically meaningful. `BuiltinRequirementKey` is the
hoistable requirement-key inst that carries that role, and
`BuiltinRequirementDecoration` is the matching decoration scanned by
the lookup helper.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `BuiltinRequirementKey` | `IRBuiltinRequirementKey` | `kindOperand` | H | (lowering) | Requirement key for a recognized built-in interface requirement; deduplicated by construction from its `kind` operand. |
| `BuiltinRequirementDecoration` | `IRBuiltinRequirementDecoration` | `kindOperand` | | (lowering) | Marks an interface requirement key with the `BuiltinRequirementKind` of the built-in requirement it represents. |

### Checkpointing and rematerialization

Reverse-mode autodiff frequently needs to read primal values at
points where they no longer naturally live. The checkpointing
opcodes record candidate stores and a marker pass either keeps them
live or replaces them with rematerialized recomputations.

| Opcode | C++ wrapper | Operands | Flags | AST origin | Summary |
| --- | --- | --- | --- | --- | --- |
| `checkpointObj` | `CheckpointObject` | (variadic, `min=1`) | | (synthesized) | Marks an object value as a candidate for primal-side checkpointing. |
| `loopExitValue` | — | (variadic, `min=1`) | | (synthesized) | Records the value of an SSA variable at a loop-exit point so reverse mode can read it. |
| `ReportCheckpointStore` | — | `storedType, originalFunc, storeRef` | | (synthesized) | Diagnostic-style marker that a checkpoint store was inserted; `storeRef` becomes `Poison` if the store is later eliminated. |
| `detachDerivative` | — | `value` | | `DetachExpr` (`detach(...)`) via `visitDetachExpr` in `slang-lower-to-ir.cpp` | Returns the operand unchanged but blocks derivative propagation through it. |

## Notable opcodes

### `MakeDiffPair`

`MakeDiffPair` is the IR encoding of constructing a differential
pair `{primal, differential}`. Its result type is a
`DifferentialPairType` (see [types.md](types.md)). The autodiff
forward-mode pass inserts `MakeDiffPair` calls whenever a primal
flows alongside its derivative, and the reverse-mode pass uses it
when stitching back together values that were temporarily split.

### `GetDifferential` / `GetPrimal`

`GetDifferential(pair)` and `GetPrimal(pair)` are the projections
that reverse `MakeDiffPair`: given a `{primal, differential}` pair
they read back the differential and primal halves respectively. The
forward-mode pass emits them when it needs only one component of a
pair it (or a callee) previously bundled, and the autodiff
simplification passes fold a `GetPrimal`/`GetDifferential` applied
directly to a `MakeDiffPair` back to the original operand. The
`GetPrimalRef` / `GetDifferentialPtr` variants project pair-of-
pointer values produced by `MakeDiffRefPair`.

### Reverse-mode context

The reverse-mode primal context is carried by *type* opcodes
(`BackwardDiffIntermediateContextType`,
`BackwardDiffMinimalContextType`, and their trivial / legacy
variants) under the `TranslatedTypeBase` group of the Type family;
they are documented in [types.md](types.md). No opcode spelled
`BackwardDiffPropagateContext` exists in the current source — the
context plumbing is the result type of
`BackwardDifferentiatePropagate` (below) rather than a standalone
context opcode.

### `ForwardDifferentiate`

`ForwardDifferentiate(baseFn)` produces a function value whose
behavior is the JVP (Jacobian-vector product) of `baseFn`. It is the
only differentiation opcode the user can directly produce from
source — the `__fwd_diff` syntactic form lowers to it.
Specialization replaces the opcode with the actual JVP function
once `baseFn` is fully known. The opcode is hoistable so that two
`__fwd_diff` references to the same function dedupe.

### `BackwardDifferentiate`

`BackwardDifferentiate` is the reverse-mode counterpart of
`ForwardDifferentiate`: it produces an adjoint function. Reverse
mode is split internally into primal, propagate, and remat phases —
the standalone `BackwardDifferentiate` opcode bundles all three for
the user-facing surface. The autodiff *unzip* pass converts each
`BackwardDifferentiate` into a triple of
`BackwardDifferentiatePrimal`, `BackwardDifferentiatePropagate`, and
`BackwardRemat` (or their trivial-variant counterparts) so that
later passes can rearrange the three phases independently.

### `BackwardDifferentiatePropagate`

The opcode that carries the reverse-mode adjoint update through the
call graph. Its result is a function whose signature accepts the
recorded primal-side state plus an output-adjoint and produces the
input-adjoints. The propagate-context family in [types.md](types.md)
defines the typed channels through which the state flows.

### `BuiltinRequirementKey`

`BuiltinRequirementKey(kind)` is the IR-level key under which a
built-in `IDifferentiable`-family requirement is stored in and
fetched from a witness table. Unlike an ordinary `StructKey` — a
distinct global symbol per requirement decl, unified across modules
by its `key_<mangled>` linkage name — the built-in key is
*hoistable*, so it is deduplicated by construction from its `kind`
operand. The same logical requirement therefore resolves to one key
inst whether it is referenced from the canonical interface
constraint, from a constraint synthesized while building a type's
`Differential`, or across the precompiled-core-module boundary; no
linkage decoration is needed because identity comes from the operand.
`getInterfaceRequirementKey` in
[slang-lower-to-ir.cpp](../../../../source/slang/slang-lower-to-ir.cpp)
emits the key and tags it with `BuiltinRequirementDecoration`;
`getInterfaceEntryByBuiltinRequirement` in
`slang-ir-autodiff.cpp` then scans that decoration to find a
requirement entry by role instead of by position. Because a built-in
requirement may be reached through dynamic dispatch, the `lookupKey`
operand of `GetDispatcher` is typed as a plain `IRInst` rather
than `IRStructKey`.

### `checkpointObj` and `ReportCheckpointStore`

`checkpointObj` marks an object value as a candidate for primal-side
checkpointing; the checkpoint pass either keeps the value live (via
ordinary use chains) or replaces it with a recomputation. The
companion `ReportCheckpointStore` records the *intent* to store —
its `storeRef` operand is a weak reference that becomes `Poison`
when the underlying store is optimized away, which the reporting
pass surfaces back to the user.

### `detachDerivative`

`detachDerivative(value)` is the IR form of the user-facing
`detach(...)` call. It returns its operand unchanged but blocks any
derivative-propagation pass from following the use; the value is
treated as a constant from the autodiff system's point of view.
Useful when the user wants to participate in differentiation only
through some operands of an expression.

## See also

- [../cross-cutting/ir-instructions.md](../cross-cutting/ir-instructions.md)
  — schema, op flags, hoistable / parent conventions.
- [types.md](types.md) — the `DifferentialPairTypeBase` and
  `TranslatedTypeBase` type families that these opcodes operate
  on, plus the `*FuncType` family for the differentiated function
  signatures.
- [values.md](values.md) — ordinary value-producing opcodes that
  the autodiff passes leave in place around the synthesized
  differentiation opcodes.
- [misc.md](misc.md) — `DifferentiableTypeAnnotation` and
  `DifferentiableTypeDictionaryItem`, the annotation-side opcodes
  that the autodiff passes attach to types.
- [../pipeline/05-ir-passes.md](../pipeline/05-ir-passes.md) — the
  forward / reverse / unzip / transcribe pass sequence and how each
  consumes and produces the opcodes documented here.
- [../../../design/autodiff.md](../../../design/autodiff.md) — design
  rationale for the split into primal / propagate / remat phases
  and for the checkpointing model.
- [../glossary.md](../glossary.md) — definitions of `differential
  pair`, `hoistable instruction`, `parent instruction`.
