Caching functions compiled with llvm-hs

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

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.htm...

On 11/07/2020, 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.htm...
Your getExecutionFunction implementation is indeed quite helpful, thank you! Cheers, Tom
participants (2)
-
Henning Thielemann
-
Tom Westerhout