CommitFest 6437 review — LLVMJIT introduce force-inlined functions

code-review · generated from canonical Markdown

Type
code-review
Source
postgresql-master
Slug
commitfest-6437-review
Tags
code-reviewcommitfestcoveragepatch-review
Updated
2026-08-28T02:09:40.850485+00:00
Verdict / Summaryneeds-review
Front matter
schema: review-report-v2
target: "CommitFest 6437 — LLVMJIT: introduce force-inlined functions"
source: postgresql-master
slug: commitfest-6437-review
base: 6e5d5680b555523a3d5c547aa22df0267860344a
head: 6e5d5680b555523a3d5c547aa22df0267860344a
diff_sha256: 6f4ded1e1f4bf59fdf22b3337cd3cab68b451306e1e192ebe073b5f4719c375c
verdict: needs-review
blocker_counts:
  verified: 0
  unverified: 0
model: glm-5.2:cloud
review_date: 2026-08-28

CommitFest 6437 — Review Report

Patch: LLVMJIT: introduce force-inlined functions File: src/backend/jit/llvm/llvmjit_expr.c (+103/-4, uncommitted staged diff) Author: p.psql@pinaraf.info (pgsql-hackers thread 635040, 2026-01-26) Reviewer seats: pg-patch-reviewer, pg-security-reviewer, pg-test-author, pg-finding-verifier (all via Amauta handoff protocol) Verdict: needs-review — two MAJOR correctness findings (F1, F2) and one MAJOR tests finding (F3) must be addressed before commit. No blockers.

Deterministic Verdict

PostgreSQL verdictneeds-review
Blockers (verified / unverified)0 / 0
MAJOR findings3 (F1 correctness, F2 correctness/robustness, F3 tests)
MINOR findings4 (F4 completeness, F5 style, F6 maintainability, F7 maintainability)
NIT findings1 (F8 docs)
Refuted0
Cannot-verify0

Rule applied: no surviving BLOCKER ⇒ not waiting-on-author; correctness MAJORs present (F1, F2) ⇒ needs-review (a softer sub-verdict cannot override a harder finding).

Summary

The patch adds a static helper llvm_try_inline_function_call() to the LLVM JIT expression compiler that manually inlines a small allowlist of strict, leakproof, two-argument comparison operators (int4eq, int8eq, date_lt, date_gt, date_le, date_ge) by emitting a direct LLVMBuildICmp instead of the generic BuildV1Call out-call. The inlining is gated on fcinfo->flinfo->fn_addr pointer equality and is only reached after the strict-argument NULL-check block (b_nonull). The author reports a 3–5% improvement on 1M dateeq calls.

The patch compiles cleanly with gcc + LLVM 21 (the referenced C functions are declared via the generated src/include/utils/fmgrprotos.h, included transitively through utils/builtins.h). The inlined IR semantics are correct for the current allowlist: int4/DateADT are int32 byval (truncation i64→i32 recovers the int32; signed LLVMIntSLT/SGT/SLE/SGE match the C comparisons, including the date infinity sentinels at INT32_MIN/MAX), int8 is int64 byval (full-width LLVMIntEQ), and resnull=false is correct on the strict path where args are guaranteed non-null.

The concerns are: (F1) the inlined path drops the fcinfo->isnull = false invariant that both BuildV1Call and the interpreter maintain; (F2) the matcher does not validate the strictness/leakproof/byval/nargs properties that make inlining legal, with no guard against drift; and (F3) the new path has no test coverage.

Findings

F1

— MAJOR — correctness — src/backend/jit/llvm/llvmjit_expr.c:79-167

fcinfo->isnull is never reset to false on the inlined path.

The inlined path bypasses BuildV1Call, which emits an IR store of 0 into FunctionCallInfoData.isnull (FIELDNO_FUNCTIONCALLINFODATA_ISNULL) before producing a result; the interpreter (execExprInterp.c:935,962,986,1010) does the same. The inlined path contains no such store, so fcinfo->isnull retains any stale value from a prior evaluation of the reusable FunctionCallInfoData.

Impact: Latent invariant violation. The op *result* (resvalue/resnull) is correct for the allowlisted operators and no existing downstream consumer reads fcinfo->isnull for these operators today, so there is no live bug. A future aggregate transition, ScalarArrayOp, or caller inspecting fcinfo->isnull after evaluation would observe stale state.

Repair (verified, compiled): Store l_sbool_const(0) into the isnull field of the fcinfo once before the operator dispatch via l_struct_gep(b, StructFunctionCallInfoData, v_fcinfo, FIELDNO_FUNCTIONCALLINFODATA_ISNULL, ...). Part of the combined refactor patch (repair_combined.diff, sha256 904c6fabb5b9b142614fdcd0ebfc407f1f4e748def3a956023dcb9e86a9e1370; validated: apply-check=0, compile COMPILE_EXIT=0 no warnings).

F2

— MAJOR — correctness/robustness — src/backend/jit/llvm/llvmjit_expr.c:79-167

Missing strictness/property guard; inlined path hardcodes resnull=false relying on an implicit strictness invariant.

The inlined path stores resnull=false without consulting argument nullness, relying entirely on the strict EEOP_FUNCEXPR_STRICT_* opcode having already null-checked all args and reached b_nonull. This is safe *only because* the executor emits EEOP_FUNCEXPR_STRICT_2 for these strict 2-arg functions (execExpr.c:2782). The matcher selects by raw C pointer fn_addr == &int4eq and does not validate the semantic guarantees (strict, leakproof, 2 args, byval, returns bool) that make the inlining legal; there is no assertion/guard bounding the allowlist against drift. A copy-paste extension adding a non-strict or non-byval function would silently miscompile.

Impact: No incorrect result with the current catalog set and current opcode selection. Latent footgun for maintainers; RLS-adjacent worst case if the invariant were ever violated.

Repair (verified, compiled): (a) Gate the inlining call at the call site (~line 838) on the opcode being one of EEOP_FUNCEXPR_STRICT/STRICT_1/STRICT_2 (structural fix), making the safety property explicit; (b) add a header doc comment above llvm_try_inline_function_call enumerating the required properties. Part of the combined refactor patch (same sha256 as F1; validated: compile COMPILE_EXIT=0).

F3

— MAJOR — tests — src/backend/jit/llvm/llvmjit_expr.c:74-169

No test coverage for the new inlined path.

No src/test/jit/ tree exists; jit=off is used in explain.sql to suppress JIT, and jit=on is not part of the default regression run. The inlined operators' results under JIT are never asserted against the interpreter, so a future regression (wrong LLVMIntPredicate, wrong truncation width, wrong resnull) could ship green.

Repair (verified; proposal-only): A complete TAP test (src/test/jit/t/001_jit_inline_ops.pl + Makefile + meson.build, plus integration edits) was produced by the test-author seat. It compares jit=on vs jit=off result sets over int4/int8/date edge values (PG_INT32_MIN/MAX, PG_INT64_MIN/MAX, date -infinity/+infinity) and NULL strict short-circuit, and asserts a JIT: EXPLAIN summary block with jit=on. perl -c returned syntax OK. Applying remains user-controlled (read-only worktree). Patch sha256 41538e8ff5e71e2423b95c372c43e551f92b41bc1134b4e3184c3fa7697689d8.

F4

— MINOR — correctness/completeness — src/backend/jit/llvm/llvmjit_expr.c:79-167

Asymmetric/incomplete allowlist. date_eq is missing while int4eq/int8eq are present; the int4/int8 ordering operators (lt/gt/le/ge) are absent. date_col = date_const falls back to the out-call while date_col < date_const is inlined. The author announced a v2 with more functions (thread: author-deferred). Repair (verified, compiled): Add date_eq plus int4/int8 lt/le/gt/ge factored through the EMIT_INT_COMPARE macro. Part of the combined patch.

F5

— MINOR — maintainability/style — src/backend/jit/llvm/llvmjit_expr.c:74-169

K&R/Allman brace style inconsistent with PostgreSQL convention; missing function doc comment; inconsistent arg comments. A reviewer on the thread explicitly requested a pgindent pass (thread: already-raised). Repair (verified, compiled): Add a PG-style doc comment; convert to factored macros. Part of the combined patch.

F6

— MINOR — maintainability — src/backend/jit/llvm/llvmjit_expr.c:79-167

Six near-identical branches duplicate the load/trunc/ICmp/ZExt/store boilerplate. Extending the allowlist (F4) multiplies the copy-paste. Repair (verified, compiled): Factor into an EMIT_INT_COMPARE(name, predicate, need_trunc) macro with LOAD_ARG(n). Part of the combined patch.

F7

— MINOR — maintainability — src/backend/jit/llvm/llvmjit_expr.c:82

Uses LLVMInt64Type() directly instead of the existing TypeDatum/l_funcvalue() helpers that the rest of the file uses. Semantically identical today but bypasses the abstraction. Repair (verified, compiled): Use l_funcvalue/TypeDatum. Part of the combined patch.

F8

— NIT — docs — src/backend/jit/llvm/llvmjit_expr.c:79-169

No doc/src/sgml/jit.sgml mention of the inlined operators. Complicates performance reasoning. Repair (verified; apply/static-check passed, doc-build not-run): Add a <sect2 id="jit-inlined-operators"> documenting the inlined comparison operators. Patch sha256 a40eec68f6c138a5c6063f78601fab292b722d0a302e0c60095061a26bc34832.

Verification Summary

All 8 findings verified (none refuted, none cannot-verify). Each verified finding carries a concrete suggested code example and a HEAD-bound unified diff. Six code findings (F1, F2, F4, F5, F6, F7) are addressed by one combined refactor patch validated in disposable scratch (apply-check=0, diff-check valid, compile COMPILE_EXIT=0 with NO warnings, gcc + LLVM 21, exact makefile flags). F3 is the test-author's TAP test (proposal-only; perl -c syntax OK). F8 is a docs patch (apply/static-check passed; full doc-build not available in this environment).

IDSeverityStatusValidationPatch sha256
F1MAJOR correctnessverifiedpassed (apply/diff/compile/targeted-test)904c6fab… (combined)
F2MAJOR correctness/robustnessverifiedpassed (apply/diff/compile/targeted-test)904c6fab… (combined)
F3MAJOR testsverifiednot-run (proposal-only; perl -c OK)41538e8f… (test files)
F4MINOR completenessverifiedpassed (apply/diff/compile/targeted-test)904c6fab… (combined)
F5MINOR maintainabilityverifiedpassed (apply/diff/compile/static-check)904c6fab… (combined)
F6MINOR maintainabilityverifiedpassed (apply/diff/compile/static-check)904c6fab… (combined)
F7MINOR maintainabilityverifiedpassed (apply/diff/compile/static-check)904c6fab… (combined)
F8NIT docsverifiednot-run (apply/diff/static-check passed; doc-build unavailable)a40eec68… (docs)

Repair Validation Summary

904c6fabb5b9b142614fdcd0ebfc407f1f4e748def3a956023dcb9e86a9e1370): HEAD-bound unified diff on src/backend/jit/llvm/llvmjit_expr.c. Subsumes the original staged diff and adds the F1 isnull reset, the F2 strict-opcode call-site guard + doc comment, the F4 symmetric allowlist, the F5 PG-style doc comment, the F6 EMIT_INT_COMPARE/LOAD_ARG factoring, and the F7 l_funcvalue/TypeDatum usage. Validated: apply-check=0, apply=0, diff-check valid, compile COMPILE_EXIT=0 with NO warnings (gcc + LLVM 21, exact makefile flags captured via make -n llvmjit_expr.o).

41538e8ff5e71e2423b95c372c43e551f92b41bc1134b4e3184c3fa7697689d8): perl -c syntax OK; make -C src/test/jit check not-run (read-only worktree; applying remains user-controlled).

a40eec68f6c138a5c6063f78601fab292b722d0a302e0c60095061a26bc34832): apply-check=0, apply=0, static SGML well-formedness check passed; full doc-build (xsltproc) not available.

Security

No memory-safety, privilege-escalation, SQL-injection, or authorization-bypass vulnerabilities found. The emitted LLVM IR is type-consistent and in-bounds (l_funcvaluep GEPs to the n'th NullableDatum slot; all operators have nargs==2; loads/stores match Datum/TypeStorageBool widths). The fn_addr pointer is read once at JIT-compile time and is immutable (no TOCTOU). The one security-adjacent concern (F2 / SEC-1) — unconditional resnull=false relying on strictness — is not currently exploitable because the executor never emits the non-strict EEOP_FUNCEXPR opcode for these strict operators; it is a latent footgun hardened by the F2 call-site guard.

Test-Gap Section

The new inlined path is reached only when JIT compiles the expression (jit=on + cost above threshold). The standard regression suite does not force JIT (jit=off in explain.sql; jit_above_cost default 100000), so the inlined operators' results are effectively untested. The proposed TAP test (F3) closes this gap by comparing jit=on vs jit=off over edge values and NULLs. A mutation-differential (swap LLVMIntEQ→LLVMIntNE and confirm int4eq parity fails) is recommended to prove the net covers the inlined path.

pgsql-hackers Thread Ledger

Status: reconciled. One thread (Thread-ID 635040, subject "LLVMJIT: introduce force-inlined functions"). Ledger report slug: commitfest-6437-review-thread-ledger.

gain on 1M dateeq calls; announces a v2 with more functions.

comparison; asks whether a v2 is planned; requests a pgindent pass.

Per-finding dispositions: F4 = author-deferred (v2 with more functions announced); F5 = already-raised (pgindent requested); F1/F2/F3/F6/F7/F8 = new. No committer reply; no v2 in the archive as of the review date.

Per-commit rollup

Single uncommitted staged diff on src/backend/jit/llvm/llvmjit_expr.c (+103/-4). No commit history to roll up.

Verified-clean section

The following non-findings were explicitly verified clean (not findings):

Aggregated Test Plan

  1. F3 TAP test (src/test/jit/t/001_jit_inline_ops.pl): apply the four

proposed files + two integration edits, build --with-llvm, run make -C src/test/jit check. Expected: PASS on base (oracle holds via BuildV1Call), PASS on HEAD (inlined path matches interpreter).

  1. Mutation-differential (recommended, optional): swap

LLVMIntEQ→LLVMIntNE in the int4eq branch and confirm int4 = (int4eq) parity FAILS while jit=off still returns correct results — proves the net covers the inlined IR.

  1. Combined C patch: compile-check passes; full behavior parity asserted by

the F3 TAP test oracle.

Changed-Code Coverage

Coverage requirement: required (one changed instrumentable path: src/backend/jit/llvm/llvmjit_expr.c). Status: measured (fresh disposable counter epoch).

MetricValue
changed executable lines77
covered changed lines77
compiler-uncovered changed lines0
uncovered changed lines0
structural uncovered changed lines0
changed lines without coverage coordinates26
changed branches14
covered changed branches14

Execution lane: host-disposable (passed). A fresh --enable-coverage --enable-cassert --with-llvm build was produced in disposable scratch; PostgreSQL started from the install bin; the 243-test standard regression suite passed (make check), plus 7 JIT-forcing queries (jit_above_cost=0, jit_inline_above_cost=0) over int4/int8/date covering all six inlined operators (EXPLAIN confirmed JIT: Functions N, Expressions true). The server was stopped to flush .gcda; gcovr 8.6 consumed the gcno/gcda with integrity checks. integrity_errors: [].

Touched functions:

73/73 covered; whole-function branches 12/12 covered.

entered; whole-function lines 261/997; whole-function branches 67/254 (the changed call-site lines 838–844 are all covered: 32 inline / 9 fallback).

Uncovered regions: none. All 77 executable changed lines and all 14 changed branches are covered. The 26 changed lines without coverage coordinates are bounded structural diagnostics — comments (/* load arg 1 */, /* compare and convert result */), declarations (LLVMValueRef v_fcinfo, v_retval;), braces ({/}), and one continuation line (&v_fcinfo_isnull);) — for which gcc emits no coverage coordinate. These are non-actionable structural exclusions, not executable uncovered code.

Non-instrumented changes: none.

Residual limitations: The coverage is qualified by the executed test set (standard regress + 7 forced-JIT queries). The proposed F3 TAP test, once applied, would provide a deterministic, repeatable JIT-path oracle; the coverage measurement here used ad-hoc forced-JIT queries because the TAP test is proposal-only.

Provenance / model routes

commitfest-6437-review-handoff-patch-reviewer, sha256 fb86946e18689f475bf30488377a393765ac1ecc3116f40668ccaea2649bace7).

commitfest-6437-review-handoff-security-reviewer, sha256 e16eae84ae4b7a2bb33ed6a5066eec0265fb87303fb70451989002fa53e741a7).

commitfest-6437-review-handoff-test-author, sha256 1d4279c89b48a3fc80156ed0318f9cc392b8cc5ed179c407157bd01d17d15a03).

commitfest-6437-review-handoff-finding-verifier, sha256 5248212d017e21baaffb328592ab0116fde92b71635f5a3126f71f0c01c1fbb3).

override glm-5.2:cloud honored for all seats).

21, gcovr 8.6).

Residual risk

The patch is correct for the current allowlist and current executor usage. The residual risk is the latent fcinfo->isnull invariant (F1) and the unguarded matcher (F2), both cheaply fixed by the combined patch, plus the absence of a regression net (F3) that would catch future IR mistakes. With F1, F2, and F3 addressed the patch is committer-ready.

Maintainer CC

Andres Freund andres@anarazel.de — JIT/LLVM area owner (natural reviewer for src/backend/jit/llvm/). The patch thread (635040) is on pgsql-hackers.

Artifact

The validated pg-review-artifact-v2 JSON is at the run scratch artifact path (review-artifact-v2.json, sha256 b6168e997df4e2ddbb7502f966f7e1946f433180f9fcd0863bdb37b90c000adf); it passed the supplied validator (validate_review_artifact.py, digest edbcb304ffbd062627196c1ee0ce3d9b60cae80dd7b201e420335fea0a31979e, exit 0).