Caching functions compiled with llvm-hs

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Caching functions compiled with llvm-hs

Tom Westerhout
Hello,


Suppose I've written some function f :: Foo -> Bar using LLVM IR. Now
I'd like to compile it on first invocation with llvm-hs and cache the
obtained function pointer. Basically something like this

f :: Foo -> Bar
f x = fImpl x
  where
    fImpl = unsafePerformIO $
      flag <- isAlreadyCompiled -- check the cache
      if flag
        fetchFunc -- get the compiled code from cache
      else
        compileWithLLVM -- build LLVM IR, compile it, and update the cache

A very primitive JIT compiler. What is the best way to do this with
llvm-hs? All standard examples using LLVM.OrcJIT or
LLVM.ExecutionEngine show how to compile a function and then
immediately execute it. I can't quite figure out a safe way to keep
the FunPtr... Big code bases like Accelerate or JuliaLang do achieve
this somehow, but are quite difficult to understand for the
uninitiated.

Any advice is highly appreciated!


Cheers,
Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Caching functions compiled with llvm-hs

Henning Thielemann

On Sat, 11 Jul 2020, Tom Westerhout wrote:

> A very primitive JIT compiler. What is the best way to do this with
> llvm-hs? All standard examples using LLVM.OrcJIT or
> LLVM.ExecutionEngine show how to compile a function and then
> immediately execute it. I can't quite figure out a safe way to keep
> the FunPtr...

As far as I know, the compiled function is valid as long as the
LLVM.ExecutionEngine exists. Thus I would define the following:

   data JITFunPtr f = JITFunPtr (ForeignPtr ExecutionEngine) (FunPtr f)

with DisposeExecutionEngine as finalizer for the ForeignPtr. After every
call of the FunPtr function you have to 'touchForeignPtr executionEngine'.

Alternatively, you could work with 'f' instead of 'FunPtr f' and add a
(touchForeignPtr executionEngine) to the imported 'f'. This is what I do
in llvm-tf:
    http://hackage.haskell.org/package/llvm-tf-9.2/docs/LLVM-ExecutionEngine.html#v:getExecutionFunction
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Caching functions compiled with llvm-hs

Tom Westerhout
In reply to this post by Tom Westerhout
On 11/07/2020, Georgi Lyubenov <[hidden email]> wrote:
> Is there something wrong with your idea? (other than ideological issues
> with unsafePerformIO - I guess then the standard approach would be to use
> some State holding your compiled functions or a Reader over an MVar holding
> your compiled functions)

The part that feels wrong here is that one has to create a new Module
for every single function. I always thought of LLVM Modules as kind of
compilation units. Or is this an okay-ish approach?

>
> The only thing you should make sure to do is add a NOINLINE to wherever the
> unsafePerformIO is, so that it doesn't get inlined and executed more than
> once.

Oh I totally forgot! Thank you for reminding!


Cheers,
Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Caching functions compiled with llvm-hs

Henning Thielemann

On Sun, 12 Jul 2020, Tom Westerhout wrote:

> On 11/07/2020, Georgi Lyubenov <[hidden email]> wrote:

>> Is there something wrong with your idea? (other than ideological issues
>> with unsafePerformIO - I guess then the standard approach would be to
>> use some State holding your compiled functions or a Reader over an MVar
>> holding your compiled functions)
>
> The part that feels wrong here is that one has to create a new Module
> for every single function. I always thought of LLVM Modules as kind of
> compilation units.

Right. Your module can contain multiple functions and they are compiled
and optimized together by LLVM. But if you want to cache every single
function then a one-function-module is fine.
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Caching functions compiled with llvm-hs

Tom Westerhout
In reply to this post by Henning Thielemann
On 11/07/2020, Henning Thielemann <[hidden email]> wrote:

>
> On Sat, 11 Jul 2020, Tom Westerhout wrote:
>
>> A very primitive JIT compiler. What is the best way to do this with
>> llvm-hs? All standard examples using LLVM.OrcJIT or
>> LLVM.ExecutionEngine show how to compile a function and then
>> immediately execute it. I can't quite figure out a safe way to keep
>> the FunPtr...
>
> As far as I know, the compiled function is valid as long as the
> LLVM.ExecutionEngine exists. Thus I would define the following:
>
>    data JITFunPtr f = JITFunPtr (ForeignPtr ExecutionEngine) (FunPtr f)
>
> with DisposeExecutionEngine as finalizer for the ForeignPtr. After every
> call of the FunPtr function you have to 'touchForeignPtr executionEngine'.
>
> Alternatively, you could work with 'f' instead of 'FunPtr f' and add a
> (touchForeignPtr executionEngine) to the imported 'f'. This is what I do
> in llvm-tf:
>
> http://hackage.haskell.org/package/llvm-tf-9.2/docs/LLVM-ExecutionEngine.html#v:getExecutionFunction
>

Your getExecutionFunction implementation is indeed quite helpful, thank you!


Cheers,
Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.
Reply | Threaded
Open this post in threaded view
|

Re: Caching functions compiled with llvm-hs

Tom Westerhout
In reply to this post by Henning Thielemann
On 12/07/2020, Henning Thielemann <[hidden email]> wrote:

>
> On Sun, 12 Jul 2020, Tom Westerhout wrote:
>> The part that feels wrong here is that one has to create a new Module
>> for every single function. I always thought of LLVM Modules as kind of
>> compilation units.
>
> Right. Your module can contain multiple functions and they are compiled
> and optimized together by LLVM. But if you want to cache every single
> function then a one-function-module is fine.
>

Ah that settles it then. Thanks a lot!


Cheers,
Tom
_______________________________________________
Haskell-Cafe mailing list
To (un)subscribe, modify options or view archives go to:
http://mail.haskell.org/cgi-bin/mailman/listinfo/haskell-cafe
Only members subscribed via the mailman list are allowed to post.