ORC C Interface & JITEventListeners

Hi,

I am working on using LLVM to compile parts of longrunning PostgreSQL
queries into native code for faster code execution. As postgres is,
nearly, entirely written in C and has long-lived (5 years) supported
branches (making the higher API stability important), I'm currently
using the C API.

I started out using MCJIT but it looks like that's slowly on the way
out. My current concern is that neither the ORC MCJIT replacement, nor
the ORC C bindings appear to provide integration into JIT event
listeners. Which I find a bit surprising - without gdb and/or profiler
integration, how is one supposed to actually use a JIT successfully?

It'd not be too bad if I had to use a small bit of, optional, code to
register a JIT event listener, but otherwise use the C API (that's what
I currently do for perf support in MCJIT), but it doesn't look like
that's an option with the ORC C bindings (RTDyldObjectLinkingLayer's
integration is a class template parameter defaulting to
DoNothingOnNotifyLoaded), and it's not used by OrcCBindingsStack.

In addition, it doesn't currently look there's C API to force the mcjit
replacement being used?

Is there interest in addressing these issues, or is the position more
generally that the C bindings aren't going to be useful enough? I'm
willing to work on that, but only if there's actual interest in
integrating things...

I'm also a bit confused about RTDyldObjectLinkingLayer's notify
integration. NotifyLoadedFtor is a class template parameter, but
NotifyFinalizedFtor is just a std::function defaulting to empty? Is
that just for historical raisins, or is there a deeper reason?

To be able to use existing JITEventListeners - it'd surely be a shame to
have to rewrite them anew - in custom stacks it also appears that
there's no easy way to call JITEventListener->NotifyFreeingObject() -
the to-be-freed objects aren't readily available in
RTDyldObjectLinkingLayer.

Greetings,

Andres Freund

Hi Andres,

I started out using MCJIT but it looks like that’s slowly on the way
out. My current concern is that neither the ORC MCJIT replacement, nor
the ORC C bindings appear to provide integration into JIT event
listeners. Which I find a bit surprising - without gdb and/or profiler
integration, how is one supposed to actually use a JIT successfully?

ORC is a set of components to build a JIT, rather than a black-box JIT like LLVM. The aim is to give clients more visibility into the workings of the JIT by hooking into compositions points (e.g. using a TransformLayer between other JIT layers) or by writing custom components, rather than being limited to the fixed callbacks provided by JITEventListener. Exactly how this will fit into the C API has not been explored yet.

Regarding debugging and profiling specifically: the long-term goal is to integrate the JIT with the dynamic loader so that JIT’d functions appear to the system the same way they would have if they had been dlopen’d libraries. This should allow existing profilers and debuggers to be used with JIT’d code.

It’d not be too bad if I had to use a small bit of, optional, code to
register a JIT event listener, but otherwise use the C API (that’s what
I currently do for perf support in MCJIT), but it doesn’t look like
that’s an option with the ORC C bindings (RTDyldObjectLinkingLayer’s
integration is a class template parameter defaulting to
DoNothingOnNotifyLoaded), and it’s not used by OrcCBindingsStack.

The C-bindings haven’t received much interest yet, so they’re only being improved slowly. Adding some callbacks should be easy though.

Is there interest in addressing these issues, or is the position more
generally that the C bindings aren’t going to be useful enough? I’m
willing to work on that, but only if there’s actual interest in
integrating things…

There’s interest, and I’d love to have some help with it. :slight_smile:

I’m also a bit confused about RTDyldObjectLinkingLayer’s notify
integration. NotifyLoadedFtor is a class template parameter, but
NotifyFinalizedFtor is just a std::function defaulting to empty? Is
that just for historical raisins, or is there a deeper reason?

Historical raisins. I’m working on a substantial ORC refactor that should be landing soon and this will make NotifyLoaded a std::function.

To be able to use existing JITEventListeners - it’d surely be a shame to
have to rewrite them anew - in custom stacks it also appears that
there’s no easy way to call JITEventListener->NotifyFreeingObject() -
the to-be-freed objects aren’t readily available in
RTDyldObjectLinkingLayer.

I think it would be easy enough to hook up the existing event listener interface to RTDyldObjectLayer, it’s just that nobody has done it yet.

  • Lang.

Hi,

Regarding debugging and profiling specifically: the long-term goal is to
integrate the JIT with the dynamic loader so that JIT'd functions appear to
the system the same way they would have if they had been dlopen'd
libraries. This should allow existing profilers and debuggers to be
used with JIT'd code.

I doubt that that'll be sufficient for profilers at least - frequently
profiling results will be analyzed when the program has been shut down.
Unless JITed objects are actually written out as proper shared objects I
don't see how profilers would understand this? There might also be
functions getting mapped to points where another function might have
been mapped to previously. I suspect this'll continue to need
something like the JITEventListener thing.

> It'd not be too bad if I had to use a small bit of, optional, code to
> register a JIT event listener, but otherwise use the C API (that's what
> I currently do for perf support in MCJIT), but it doesn't look like
> that's an option with the ORC C bindings (RTDyldObjectLinkingLayer's
> integration is a class template parameter defaulting to
> DoNothingOnNotifyLoaded), and it's not used by OrcCBindingsStack.

The C-bindings haven't received much interest yet, so they're only
being improved slowly. Adding some callbacks should be easy though.

Cool.

> Is there interest in addressing these issues, or is the position more
> generally that the C bindings aren't going to be useful enough? I'm
> willing to work on that, but only if there's actual interest in
> integrating things...

There's interest, and I'd love to have some help with it. :slight_smile:

Ok.

I'm quite concerned about the API stability around all of this. Postgres
releases yearly, and supports 5 years of release branches (so there's 5
release branches most of the time). Having to constantly adapt to
changing LLVM APIs in each of those release branches and the dev branch,
is going to make using LLVM pretty painful. If working on the C API is
the best way to address that concern, I'm willing to do some of that.

>To be able to use existing JITEventListeners - it'd surely be a shame to
> have to rewrite them anew - in custom stacks it also appears that
> there's no easy way to call JITEventListener->NotifyFreeingObject() -
> the to-be-freed objects aren't readily available in
> RTDyldObjectLinkingLayer.

I think it would be easy enough to hook up the existing event listener
interface to RTDyldObjectLayer, it's just that nobody has done it yet.

I'll take a stab then.

- Andres

Hi Andres,

Unless JITed objects are actually written out as proper shared objects I
don’t see how profilers would understand this?

Yes - The objects would need to be written out. The key point is to try to keep the semantics and formats as close as possible to the statically compiled equivalent.

There might also be functions getting mapped to points where another function might have
been mapped to previously.

This will depend on the nature of the specific JIT being constructed. For JITs that do re-compilation of functions (rather than just deferring the initial compilation until first call) we will need a mechanism to communicate what has happened to the tools.

I suspect this’ll continue to need something like the JITEventListener thing.

When I say that we don’t want a JITEventListener I mean that we don’t want the JITEventListener interface itself (or any fixed set of callbacks). There’s no dispute that we need general visibility into the JIT though. Decomposing the JIT into components is meant to improve that.

I’m quite concerned about the API stability around all of this. Postgres
releases yearly, and supports 5 years of release branches (so there’s 5
release branches most of the time). Having to constantly adapt to
changing LLVM APIs in each of those release branches and the dev branch,
is going to make using LLVM pretty painful. If working on the C API is
the best way to address that concern, I’m willing to do some of that.

That’s a fair concern. LLVM policy is that we don’t guarantee stability in either the C or C++ APIs. However we don’t make change for changes sake (especially not in the C APIs), so once we have a decent ORC C API I’d expect the churn to be infrequent and hopefully minimally disruptive.

Getting to a decent ORC C API is another question. Nobody has invested much effort in that yet. Once someone does I’d expect plenty of churn until a decent, tested design emerges.

Cheers,
Lang.