Search code examples
f#llvmdllimportllvm-c++-apillvm-fs

How can I call this native function from F#? (LLVMCreateJITCompilerForModule)


I'm using the llvm-fs bindings and one method I would like to call is createJITCompilerForModule which is an extern to the native method LLVMCreateJITCompilerForModule in the LLVM C api. The author of llvm-fs has stated he can't make a 'nice' version of this function call in F#:

createJITCompilerForModule in llvm-fs:Generated.fs:

[<DllImport(
    "LLVM-3.1.dll",
    EntryPoint="LLVMCreateJITCompilerForModule",
    CallingConvention=CallingConvention.Cdecl,
    CharSet=CharSet.Ansi)>]
extern bool createJITCompilerForModuleNative(
    void* (* LLVMExecutionEngineRef* *) OutJIT,
    void* (* LLVMModuleRef *) M,
    uint32 OptLevel,
    void* OutError)
// I don't know how to generate an "F# friendly" version of LLVMCreateJITCompilerForModule

Do you know how I would call this function from F#, or even what the native one does? It looks like it has an 'out parameter' for OutJIT (as the native code reassigns a thing the void* points to). Here is the native function:

LLVMCreateJITCompilerForModule in llvm-c:ExecutionEngineBindings.cpp:

LLVMBool LLVMCreateJITCompilerForModule(LLVMExecutionEngineRef *OutJIT,
                                        LLVMModuleRef M,
                                        unsigned OptLevel,
                                        char **OutError) {
    std::string Error;
    EngineBuilder builder(unwrap(M));
    builder.setEngineKind(EngineKind::JIT)
           .setErrorStr(&Error)
           .setOptLevel((CodeGenOpt::Level)OptLevel);
    if (ExecutionEngine *JIT = builder.create()) {
        *OutJIT = wrap(JIT);
        return 0;
    }
    *OutError = strdup(Error.c_str());
    return 1;
}

Solution

  • The actual function I wanted to use was a special hand made one as it couldn't be generated. I've put it here as an example of how to call it:

    llvm-fs:ExecutionEngine.fs

    let private createEngineForModuleFromNativeFunc
            (nativeFunc : (nativeint * nativeint * nativeint) -> bool)
            (moduleRef : ModuleRef) =
    
        use outEnginePtr = new NativePtrs([|0n|])
        use outErrPtr = new NativePtrs([|0n|])
        let createFailed =
                nativeFunc (
                    outEnginePtr.Ptrs,
                    moduleRef.Ptr,
                    outErrPtr.Ptrs)
        if createFailed then
            let errStr = Marshal.PtrToStringAuto (Marshal.ReadIntPtr outErrPtr.Ptrs)
            Marshal.FreeHGlobal (Marshal.ReadIntPtr outErrPtr.Ptrs)
            failwith errStr
        else
            ExecutionEngineRef (Marshal.ReadIntPtr outEnginePtr.Ptrs)
    
    let createJITCompilerForModule (modRef : ModuleRef) (optLvl : uint32) =
        let f (engPtr, modPtr, outErrPtr) =
            createJITCompilerForModuleNative (engPtr, modPtr, optLvl, outErrPtr)
    
        createEngineForModuleFromNativeFunc f modRef