Continuing through the jit_* family in logical clusters: this pair decides what JIT compiles, and you have already seen their names, because they are the “Expressions true, Deforming true” printed in every JIT block the last post showed. Both are boolean, context user, default on, part of the original PostgreSQL 11 JIT work, and filed under Developer Options, the section of the documentation that comes with a standing “not on a production database” warning.

The two names are the two jobs Andres Freund identified as the executor’s bottlenecks when he built the JIT subsystem, which is why these are the two things it compiles. Expression compilation replaces the executor’s interpreted walk through expression opcodes, the machinery that evaluates your WHERE clauses, target lists, and aggregate transitions, with native code generated for those exact expressions. Tuple deforming is the step before that: converting a stored tuple’s packed bytes into the arrays of datums and nulls the executor works with. The generic deformer has to handle any tuple shape, checking the null bitmap, walking variable-width values, and honoring alignment one attribute at a time; the JIT emits a deformer specialized to the exact tuple descriptor at hand, with the generality compiled away.

The pair looks symmetric and isn’t, which a session’s worth of toggling makes plain. With both on, a forced-JIT scan of a test table reports:

1 JIT:
2 Functions: 5
3 Options: Inlining false, Optimization false, Expressions true, Deforming true

Turn off jit_tuple_deforming and you get what you’d expect, the same block minus the deform functions:

1 JIT:
2 Functions: 3
3 Options: Inlining false, Optimization false, Expressions true, Deforming false

Turn off jit_expressions instead, and the JIT block disappears entirely. Not “Expressions false”; gone. Deforming code is only ever generated in the course of compiling the expressions that consume the deformed tuples, so with expression compilation off there is nothing to attach a deformer to, and the provider is never invoked at all: jit_compile_expr() returns before touching it. “Expressions false, Deforming true” is a state the Options line cannot display. The timing line has been telling you about this dependency all along, incidentally; Deform appears as a parenthetical inside Generation because the deform counter is a subset of the generation counter.

That asymmetry is also the reason these parameters earn their keep, because their use is bug isolation, and it dictates the procedure. Suspecting JIT of a crash or wrong results: first SET jit = off for the session; if the problem vanishes, JIT is implicated. Then jit = on with SET jit_tuple_deforming = off; if that fixes it, the deform compiler is your culprit, and if it doesn’t, the expression compiler is, since deforming can’t run without it. Two settings, three outcomes, one suspect, all without a restart, and a report to pgsql-bugs that says “JIT tuple deforming” instead of “JIT, somewhere” is a materially better bug report.

That is the whole legitimate use. These are not tuning knobs: if JIT compilation is costing your workload more than it returns, the remedy lives in the cost thresholds or in jit = off, not in shaving off compilation features one boolean at a time. Leave both on, remember which one you can test in isolation, and be glad they take effect with a SET on the day you need them.