Обсуждение: Add missing JIT inline pass for llvm>=17

Поиск
Список
Период
Сортировка

Add missing JIT inline pass for llvm>=17

От
Anthonin Bonnefoy
Дата:
Hi,

While reviewing the llvm-22 patches[0], I've tried to trigger JIT
inlining using the provided check_jit_code.sh script. The script
generates bc for both jit_inline_above_cost=0 and
jit_inline_above_cost=100000 and compares the generated code.

However, the generated bitcode were more or less the same, the only
minor difference being the "inlining" version had additional strings.
I've made sure to install through autoconf as meson doesn't generate
and install bitcode files yet[1].

Debugging showed the code correctly going through llvm_inline and
correctly importing the functions in the module. However, I'm using
llvm21 and only the "default<O0>,mem2reg" passes are used, without any
inlining passes.

With llvm<17, the passes were configured as follow:
- No optimisation and no inlining: always-inline
- No optimisation and inlining: inline
- Optimisation: O3 (which includes inline)

The attached patch adds the inline pass when jit inlining is triggered
without optimisation, as was done with LLVM<17.

I didn't add the always-inline pass as I don't think this can have any
effect. From what I understand, the functions need to be imported
through llvm_inline for LLVM to be able to inline them. I've tested by
tagging int4mod as always inline, and the generated bc was still
calling the function despite the always-inline pass.

Regards,
Anthonin Bonnefoy

[0]:
https://www.postgresql.org/message-id/flat/CA%2BhUKGJTumad75o8Zao-LFseEbt%3DenbUFCM7LZVV%3Dc8yg2i7dg%40mail.gmail.com
[1]: https://www.postgresql.org/message-id/flat/206b001d-1884-4081-bd02-bed5c92f02ba%40eisentraut.org

Вложения

Re: Add missing JIT inline pass for llvm>=17

От
Thomas Munro
Дата:
On Wed, Jan 14, 2026 at 9:02 PM Anthonin Bonnefoy
<anthonin.bonnefoy@datadoghq.com> wrote:
> The attached patch adds the inline pass when jit inlining is triggered
> without optimisation, as was done with LLVM<17.

Oof, right.  Commit message should mention that this was an oversight
in 76200e5e.  I think we should back-patch this.

     if (context->base.flags & PGJIT_OPT3)
         passes = "default<O3>";
+    else if (context->base.flags & PGJIT_INLINE
+             && !(context->base.flags & PGJIT_OPT3))
+        /* if doing inlining, but no expensive optimization, add inline pass */
+        passes = "default<O0>,mem2reg,inline";
     else
         passes = "default<O0>,mem2reg";

That expression after && is redundant with the "else".

> I didn't add the always-inline pass as I don't think this can have any
> effect. From what I understand, the functions need to be imported
> through llvm_inline for LLVM to be able to inline them. I've tested by
> tagging int4mod as always inline, and the generated bc was still
> calling the function despite the always-inline pass.

What about llvmjit_deform.c's use of l_callsite_alwaysinline()?



Re: Add missing JIT inline pass for llvm>=17

От
Andreas Karlsson
Дата:
Great find! Sadly shows how little people actually use JIT.

On 1/14/26 9:02 AM, Anthonin Bonnefoy wrote:
> I didn't add the always-inline pass as I don't think this can have any
> effect. From what I understand, the functions need to be imported
> through llvm_inline for LLVM to be able to inline them. I've tested by
> tagging int4mod as always inline, and the generated bc was still
> calling the function despite the always-inline pass.

If inline-always does not do anything it should be removed on older LLVM 
versions too. I do not think we should be having pre- and post-LLVM 17 
run different passes. But as Thomas pointed out inline-always is likely 
used for tuple deforming.

Andreas




Re: Add missing JIT inline pass for llvm>=17

От
Anthonin Bonnefoy
Дата:
On Thu, Jan 15, 2026 at 4:20 AM Andreas Karlsson <andreas@proxel.se> wrote:
> If inline-always does not do anything it should be removed on older LLVM
> versions too. I do not think we should be having pre- and post-LLVM 17
> run different passes. But as Thomas pointed out inline-always is likely
> used for tuple deforming.

Right, I've missed the l_callsite_alwaysinline for varsize_any.
Testing with the following query to trigger a call to varsize_any:

create table test_always_inline(id integer, data text);
select data, id FROM test_always_inline;

The generated bc were identical (attached with the message), with and
without always-inline with varsize_any not being inlined. I think this
is the same issue as with external functions. varsize_any is defined
in postgres/access/common/heaptuple.bc, and the function needs to be
imported for LLVM to be able to inline it. Without going through
llvm_inline and importing the functions, there's no inlining doable.

Maybe the issue is that always-inline functions should be inlined,
even with the non-optimized case (at least, that's what the configured
passes seem to imply)? But that would require calling llvm_inline,
which kind of defeats the purpose of having a dedicated PGJIT_INLINE
flag and threshold.

I've updated the patch with the simplified PGJIT_INLINE check and the
commit message change. I've added a separate patch to remove the
always-inline pass pre-LLVM 17 if we want to go that way.

Вложения

Re: Add missing JIT inline pass for llvm>=17

От
Álvaro Herrera
Дата:
On 2026-Jan-15, Andreas Karlsson wrote:

> Great find! Sadly shows how little people actually use JIT.

I disagree.  Given that JIT is enabled by default, I think lots of
people use it.  What they don't do, is realize that things are slower
than they could be -- much less try to figure out why.

But yes, this was a great find.

-- 
Álvaro Herrera         PostgreSQL Developer  —  https://www.EnterpriseDB.com/



Re: Add missing JIT inline pass for llvm>=17

От
Andres Freund
Дата:
Hi,

Good catch in noticing this.

On 2026-01-15 10:40:37 +0100, Anthonin Bonnefoy wrote:
> On Thu, Jan 15, 2026 at 4:20 AM Andreas Karlsson <andreas@proxel.se> wrote:
> > If inline-always does not do anything it should be removed on older LLVM
> > versions too. I do not think we should be having pre- and post-LLVM 17
> > run different passes. But as Thomas pointed out inline-always is likely
> > used for tuple deforming.
> 
> Right, I've missed the l_callsite_alwaysinline for varsize_any.
> Testing with the following query to trigger a call to varsize_any:
> 
> create table test_always_inline(id integer, data text);
> select data, id FROM test_always_inline;
> 
> The generated bc were identical (attached with the message), with and
> without always-inline with varsize_any not being inlined. I think this
> is the same issue as with external functions. varsize_any is defined
> in postgres/access/common/heaptuple.bc, and the function needs to be
> imported for LLVM to be able to inline it. Without going through
> llvm_inline and importing the functions, there's no inlining doable.

Right - but the heuristic inline pass might *still* not inline even after
llvm_inline...


> Maybe the issue is that always-inline functions should be inlined,
> even with the non-optimized case (at least, that's what the configured
> passes seem to imply)? But that would require calling llvm_inline,
> which kind of defeats the purpose of having a dedicated PGJIT_INLINE
> flag and threshold.

No, it doesn't. E.g. the generated deform function should be inlined even if
we don't do the more expansive inlining.


> I've updated the patch with the simplified PGJIT_INLINE check and the
> commit message change. I've added a separate patch to remove the
> always-inline pass pre-LLVM 17 if we want to go that way.

I'm strongly against removing the always inline pass, I see absolutely no
reason for doing that. The whole point of always inline is that it happens
unconditionally. It's not an expensive pass either.

Greetings,

Andres Freund



Re: Add missing JIT inline pass for llvm>=17

От
Anthonin Bonnefoy
Дата:
On Thu, Jan 15, 2026 at 2:51 PM Andres Freund <andres@anarazel.de> wrote:
> I'm strongly against removing the always inline pass, I see absolutely no
> reason for doing that. The whole point of always inline is that it happens
> unconditionally. It's not an expensive pass either.

I've looked into more details on what was provided by 'default<O0>',
and it turns out it includes an always-inline pass[0]. This is also
visible when using debug-pass-manager:

llvm-as < /dev/null | opt -disable-output --passes='default<O0>'
-debug-pass-manager
Running analysis: InnerAnalysisManagerProxy<FunctionAnalysisManager,
Module> on [module]
Running pass: AlwaysInlinerPass on [module]
Running analysis: ProfileSummaryAnalysis on [module]
Running pass: CoroConditionalWrapper on [module]
Running pass: VerifierPass on [module]
Running analysis: VerifierAnalysis on [module]

With the pre-LLVM17 legacy pass manager, that doesn't seem to be the
case[1] (despite the confusing comment? Inliner is only set by
LLVMPassManagerBuilderUseInlinerWithThreshold).

So, with 'default<O0>,mem2reg', we replicate the same behaviour as
pre-LLVM17 as it includes the always-inline pass.

I've updated the patch to only add the inline pass when PGJIT_INLINE
is on. I've also added a comment to mention that always-inline is
included in O0.

[0]:
https://github.com/llvm/llvm-project/blob/701040d48f759369dce755f185a21aa6b92ba3ae/llvm/lib/Passes/PassBuilderPipelines.cpp#L2360-L2365
[1]: https://github.com/llvm/llvm-project/blob/release/16.x/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp#L290-L313

Вложения

Re: Add missing JIT inline pass for llvm>=17

От
Michael Banck
Дата:
Hi,

On Thu, Jan 15, 2026 at 12:26:23PM +0100, Álvaro Herrera wrote:
> On 2026-Jan-15, Andreas Karlsson wrote:
> > Great find! Sadly shows how little people actually use JIT.
>
> I disagree.  Given that JIT is enabled by default, I think lots of
> people use it.

Well, not sure about that - all of the three major hyperscalers disable
JIT in their managed Postgres offerings (or at least used to when I last
checked), and those are a major chunk of usage these days. Also, both
the RPM and (since recently) the Debian/Ubuntu community packages have
factored out the LLVM/jit part into their own packages and AFAIK they do
not get installed by default.

So while the GUC is on by default, a lot of users might not use JIT
these days and not know either way.

> What they don't do, is realize that things are slower
> than they could be -- much less try to figure out why.

Right.


Michael