Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to create a function pointer and call it? #833

Closed
oneengineer opened this issue Dec 30, 2019 · 38 comments
Closed

How to create a function pointer and call it? #833

oneengineer opened this issue Dec 30, 2019 · 38 comments

Comments

@oneengineer
Copy link

oneengineer commented Dec 30, 2019

int (*sum_func)(int, int) = (int (*)(int, int))LLVMGetFunctionAddress(engine, "sum");

I need to call LLVMGetFunctionAddress like the C code above in Java, however it returns a pointer and I need to convert it to (int (*)(int, int)) function.

More specifically, I guess the code would be like:

long funAddr = LLVMGetFunctionAddress(engine, "sum")

new Pointer {

   //someshow pass the funAddr to it

   public native void call( /*parameters here*/ );

}
@saudet
Copy link
Member

saudet commented Dec 31, 2019

Are you saying that you would like to call that function using your native platform's C/C++ compiler?

@oneengineer
Copy link
Author

Are you saying that you would like to call that function using your native platform's C/C++ compiler?

I updated the question.
No I want to call the function pointer in Java. But I don't know how to do it.

@saudet
Copy link
Member

saudet commented Dec 31, 2019

Since you're using LLVM, you might as well keep using that. There is an example of function calling here:
https://github.com/bytedeco/javacpp-presets/blob/master/llvm/samples/Fac.java

@oneengineer
Copy link
Author

oneengineer commented Dec 31, 2019

I have read this article and tried to follow it to try this lib, however I have to (I don't know other ways) to use LLVMGetFunctionAddress instead of LLVMRunFunction.

It seems LLVMRunFunction of MCJIT only support 1 argument function?
In the stderr it print, it tells use LLVMGetFunctionAddress like the C code above instead of LLVMRunFunction.

I thought converting pointer to a function pointer is quite often in C.
Can I do the same thing in javacpp?

@saudet
Copy link
Member

saudet commented Dec 31, 2019

I see, you're talking about the limitation of LLVM discussed on this page?
https://www.owenstephens.co.uk/blog/2018/09/25/getting-started-with-the-newer-llvm-c-api.html
To do the same thing from Java with JavaCPP, we still need to use a C++ compiler, but if that's OK with you then yes we can define a new FunctionPointer that matches that signature. There's an example of that in the unit tests here:
https://github.com/bytedeco/javacpp/blob/master/src/test/java/org/bytedeco/javacpp/PointerTest.java

@oneengineer
Copy link
Author

oneengineer commented Dec 31, 2019

I created function wrapper like this:

public class Fn extends FunctionPointer {
        /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
        public    Fn(Pointer p) { super(p); }
        public    Fn(Long addr) {
            this.address = addr;
        }

        //protected Fn() { allocate(); }
        //private native void allocate();
        public native int call( int a,
                                 int b);
    }

call it with fn = new Fn( funAddr );fn.call(7, 9)

and get error java.lang.UnsatisfiedLinkError: Fn.call(II)I

we still need to use a C++ compiler

The function is compiled in LLVM's JIT at run time. I think I cannot use c++ compiler?

Also I didn't find ORC JIT in javacpp-presets LLVM. It seems OrcBindings.h is not included?

@saudet
Copy link
Member

saudet commented Jan 2, 2020

and get error java.lang.UnsatisfiedLinkError: Fn.call(II)I

That just means you didn't run the Builder. Check the README.md file for some instructions.

Also I didn't find ORC JIT in javacpp-presets LLVM. It seems OrcBindings.h is not included?

That seems to be part of the C API so it shouldn't be too hard to add.
Contributions are welcome!

@oneengineer
Copy link
Author

Thanks, I might try to add OrcBindings.h into llvm

@saudet
Copy link
Member

saudet commented Jan 4, 2020

There were other header files missing, so I've added all of them in one fell swoop. Please give it a try with the snapshots: http://bytedeco.org/builds/

In this case though, for your custom function pointer, you'll still need use JavaCPP to generate some JNI and use your C++ compiler to build a native library, either manually or via JavaCPP. Let me know if you're having any issues with that.

@saudet saudet removed the help wanted label Jan 5, 2020
@oneengineer
Copy link
Author

Hi saudet

I got trouble to try the 1.5.3 snapshot.
I tried to build an example (of opencv) following the guide written at http://bytedeco.org/builds/
I copy the exactly the same pom.xml and run command mvn -Djavacpp.platform=linux-x86_64 -U -X compile

However I got following error.


[DEBUG] Could not find metadata org.bytedeco:opencv:4.1.2-1.5.3-SNAPSHOT/maven-metadata.xml in local (/home/myusername/.m2/repository)
[DEBUG] Skipped remote request for org.bytedeco:opencv:4.1.2-1.5.3-SNAPSHOT/maven-metadata.xml, already updated during this session.
[DEBUG] Dependency collection stats: {ConflictMarker.analyzeTime=396472, ConflictMarker.markTime=230463, ConflictMarker.nodeCount=39, ConflictIdSorter.graphTime=230235, ConflictIdSorter.to
psortTime=163343, ConflictIdSorter.conflictIdCount=7, ConflictIdSorter.conflictIdCycleCount=0, ConflictResolver.totalTime=1834960, ConflictResolver.conflictItemCount=38, DefaultDependencyC
ollector.collectTime=4780472490, DefaultDependencyCollector.transformTime=3868901}
[DEBUG] org.bytedeco.opencv:stitching:jar:1.5.3-SNAPSHOT
[DEBUG]    org.bytedeco:opencv-platform:jar:4.1.2-1.5.3-SNAPSHOT:compile
[DEBUG]       org.bytedeco:openblas-platform:jar:0.3.7-1.5.3-SNAPSHOT:compile
[DEBUG]          org.bytedeco:openblas:jar:0.3.7-1.5.3-SNAPSHOT:compile
[DEBUG]          org.bytedeco:openblas:jar:linux-x86_64:0.3.7-1.5.3-SNAPSHOT:compile
[DEBUG]       org.bytedeco:opencv:jar:4.1.2-1.5.3-SNAPSHOT:compile
[DEBUG]          org.bytedeco:javacpp:jar:1.5.3-SNAPSHOT:compile
[DEBUG]       org.bytedeco:opencv:jar:linux-x86_64:4.1.2-1.5.3-SNAPSHOT:compile
[DEBUG] Using transporter WagonTransporter with priority -1.0 for https://oss.sonatype.org/content/repositories/snapshots
[DEBUG] Using connector BasicRepositoryConnector with priority 0.0 for https://oss.sonatype.org/content/repositories/snapshots
Downloading from sonatype-nexus-snapshots: https://oss.sonatype.org/content/repositories/snapshots/org/bytedeco/opencv/4.1.2-1.5.3-SNAPSHOT/opencv-4.1.2-1.5.3-SNAPSHOT-linux-x86_64.jar
[DEBUG] Writing tracking file /home/myusername/.m2/repository/org/bytedeco/opencv/4.1.2-1.5.3-SNAPSHOT/opencv-4.1.2-1.5.3-SNAPSHOT-linux-x86_64.jar.lastUpdated
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  5.221 s
[INFO] Finished at: 2020-01-15T11:52:40+08:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal on project stitching: Could not resolve dependencies for project org.bytedeco.opencv:stitching:jar:1.5.3-SNAPSHOT: Could not find artifact org.bytedeco:open$
v:jar:linux-x86_64:4.1.2-1.5.3-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots) -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal on project stitching: Could not resolve dependencies for project org.bytedeco.opencv:stitching:jar:1.5.3-SNA$
SHOT: Could not find artifact org.bytedeco:opencv:jar:linux-x86_64:4.1.2-1.5.3-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots)

Looks like it cannot find maven-metadata?
But from my .m2 I can see:


            ├── opencv
            │   └── 4.1.2-1.5.3-SNAPSHOT
            │       ├── maven-metadata-sonatype-nexus-snapshots.xml
            │       ├── maven-metadata-sonatype-nexus-snapshots.xml.sha1
            │       ├── opencv-4.1.2-1.5.3-20191229.221104-400.jar
            │       ├── opencv-4.1.2-1.5.3-20191229.221104-400.jar.sha1
            │       ├── opencv-4.1.2-1.5.3-20191229.221104-400.pom
            │       ├── opencv-4.1.2-1.5.3-20191229.221104-400.pom.sha1
            │       ├── opencv-4.1.2-1.5.3-SNAPSHOT.jar
            │       ├── opencv-4.1.2-1.5.3-SNAPSHOT-linux-x86_64.jar.lastUpdated
            │       ├── opencv-4.1.2-1.5.3-SNAPSHOT.pom
            │       ├── _remote.repositories
            │       └── resolver-status.properties
            └── opencv-platform
                └── 4.1.2-1.5.3-SNAPSHOT
                    ├── maven-metadata-sonatype-nexus-snapshots.xml
                    ├── maven-metadata-sonatype-nexus-snapshots.xml.sha1
                    ├── opencv-platform-4.1.2-1.5.3-20191229.221123-400.jar
                    ├── opencv-platform-4.1.2-1.5.3-20191229.221123-400.jar.sha1
                    ├── opencv-platform-4.1.2-1.5.3-20191229.221123-400.pom
                    ├── opencv-platform-4.1.2-1.5.3-20191229.221123-400.pom.sha1
                    ├── opencv-platform-4.1.2-1.5.3-SNAPSHOT.jar
                    ├── opencv-platform-4.1.2-1.5.3-SNAPSHOT.pom
                    ├── _remote.repositories
                    └── resolver-status.properties

@saudet
Copy link
Member

saudet commented Jan 15, 2020 via email

@oneengineer
Copy link
Author

I changed to <version>4.2.0-1.5.3-SNAPSHOT</version>. Still got error


ERROR] Failed to execute goal on project stitching: Could not resolve dependencies for project org.bytedeco.opencv:stitching:jar:1.5.3-SNAPSHOT: Could not find artifact org.bytedeco:openc
v:jar:linux-x86_64:4.2.0-1.5.3-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots) -> [Help 1]

In fact I try to add llvm dependency like


 <dependencies>
 10         <dependency>
 11             <groupId>org.bytedeco</groupId>
 12             <artifactId>llvm-platform</artifactId>
 13             <version>9.0.0-1.5.3-SNAPSHOT</version>
 14         </dependency>
 15         <!-- ... -->
 16     </dependencies>

Running same command, but got similar error

[ERROR] Failed to execute goal on project mycom: Could not resolve dependencies for project org.mytest.hello:mycom:jar:1.5-SNAPSHOT: Could not find artifact org.bytedeco:llvm:jar:linux-x8$
_64:9.0.0-1.5.3-SNAPSHOT in sonatype-nexus-snapshots (https://oss.sonatype.org/content/repositories/snapshots) -> [Help 1]

@saudet
Copy link
Member

saudet commented Jan 15, 2020

Make sure that the snapshot repository is in your settings: http://bytedeco.org/builds/

@saudet
Copy link
Member

saudet commented Jan 17, 2020

Also, the Nexus server of Sonatype OSS is configured to remove snapshot artifacts after 3 days, so they might have been only temporarily missing.

@saudet
Copy link
Member

saudet commented Apr 15, 2020

Version 1.5.3 has been released, so please try again with those artifacts:
https://search.maven.org/search?q=org.bytedeco%20llvm

@saudet
Copy link
Member

saudet commented Jul 25, 2020

JNA uses libffi to do that, so we could do the same with JavaCPP.
It would be pretty easy to create presets for libffi's very simple API:
http://www.chiark.greenend.org.uk/doc/libffi-dev/html/Using-libffi.html
Someone needs to do it though, but if you would like to give it a try, contributions are welcome!

@saudet
Copy link
Member

saudet commented Jul 25, 2020

/cc @yukoba

@Neiko2002
Copy link
Member

JNA has quite an overhead. I've created a small library which combines LLVM compilation and native function invocation via JNR.
https://github.com/WhenPerformanceMatters/llvm-jnr

This makes all the calls type-safe and reduces the overhead since JNR is better than JNA. There is a small benchmark based on @yukoba pull-request. It suggests for small matrices (20x20) the matrix multiplication via LLVM+Polly+JNR is faster than calling MKL with its JIT optimizer.

@saudet
Copy link
Member

saudet commented Jul 29, 2020

@Neiko2002 Thanks for testing! Have you also tried with "just" libffi?

BTW, it looks like https://github.com/WhenPerformanceMatters/llvm-jnr isn't accessible publicly.

@Neiko2002
Copy link
Member

Neiko2002 commented Jul 30, 2020

My fault, it is now public.

What do you mean with just libffi? JNR is based on jffi which is a JNI binding for the libffi.

@saudet
Copy link
Member

saudet commented Jul 30, 2020

My fault, it is now public.

Looks good, thanks! MKL is notably not super fast on AMD processors, so you might want to run that on an Intel box as well, where it's most likely going to the fastest even for small matrices. Another thing, apparently MKL's JIT doesn't use threads so setting the number of threads probably doesn't do anything (for small matrices that is):

Currently, all JIT GEMM kernels are single-threaded.

https://software.intel.com/content/www/us/en/develop/articles/intel-math-kernel-library-improved-small-matrix-performance-using-just-in-time-jit-code.html

What do you mean with just libffi? JNR is based on jffi which is a JNI binding for the libffi.

Well, with some small wrappers using JavaCPP. Actually, if you're after speed, using FunctionPointer is most likely going to be faster than even JNR anyway.

@Neiko2002
Copy link
Member

Well, with some small wrappers using JavaCPP. Actually, if you're after speed, using FunctionPointer is most likely going to be faster than even JNR anyway.

True, but I want to be able to parse, compile, and run LLVM IR without any c-compiler. I don't think a JavaCPP wrapper around libffi will be much faster than jffi. The biggest problem are all the optimized invocation functions depending on the return type and input parameters.

You are right MKL JIT is single-threaded as is the LLVM and Java implementation. More importantly, is the performance gain of JNR vs JNA for small matrices.

@saudet
Copy link
Member

saudet commented Jul 30, 2020 via email

@saudet
Copy link
Member

saudet commented Jul 30, 2020

BTW, I think this is the kind of thing @cypof was trying to achieve in pull bytedeco/javacpp#138.

@Neiko2002
Copy link
Member

I'm sure we can use Clang as a JIT C++ compiler, somehow.

My biggest problem with the clang c-API is the absence of functions to translate C/C++ to LLVM IR. We can do all the code analysis with it but never go a step further in the compiler chain, which is a pity. If javacpp would produce LLVM IR we could use the LLVM preset as a runtime compiler like @cypof suggested, but that's to much work. A other way is the cpp API of clang where we have CompilerInstance and EmitLLVMOnlyAction.

BTW, I think this is the kind of thing @cypof was trying to achieve in pull bytedeco/javacpp#138.

Making javacpp build its self at runtime with the help of preinstalled compilers is a good step. Even calling gradle or maven from within java to do the heavy lifting is sufficient as long as javacpp setups all the dependencies (e.g. download Gradle via its wrapper).

Different topic. I also tried using LLVM ORC but the LLVMOrcAddEagerlyCompiledIR method never called the provided symbol resolver callback function. If you like I can provide a small repository to show you the code.

@saudet
Copy link
Member

saudet commented Aug 1, 2020

My biggest problem with the clang c-API is the absence of functions to translate C/C++ to LLVM IR. We can do all the code analysis with it but never go a step further in the compiler chain, which is a pity. If javacpp would produce LLVM IR we could use the LLVM preset as a runtime compiler like @cypof suggested, but that's to much work. A other way is the cpp API of clang where we have CompilerInstance and EmitLLVMOnlyAction.

Well, the thing is, Java needs to load JNI code from a library file anyway, so it doesn't really matter that we have to use an external tool to create a library, and then load that library. We're not losing anything. We're not going to be able to do in-memory JIT with JNI either way. It's actually pretty easy to run a bundled version of clang that way with JavaCPP at runtime and everything. Now, one thing we need to add to make this actually work though is to build it with libc++... Interested in helping out with that? :)

Different topic. I also tried using LLVM ORC but the LLVMOrcAddEagerlyCompiledIR method never called the provided symbol resolver callback function. If you like I can provide a small repository to show you the code.

Sure, please post anything you have! I'm sure @oneengineer will be interested in looking at it.

@oneengineer
Copy link
Author

Actually, LLVMOrcAddEagerlyCompiledIR is the reason I ask for "be able to provide callback function in pure java".
Yes there is no default (as far as I know) callback function for LLVMOrcAddEagerlyCompiledIR in provided LLVM C-API.

(due to my limited knowledge, the content below might has mistakes)
Definitely we can write a piece of C code to create a default callback function and expose it with JavaCPP or write a piece of Java and compile it to C through with JavaCPP.

However it is not convenient, even though it is only one step. You cannot do it on runtime. You have to use a C/C++ compiler to compile it through JavaCPP implicitly.

I hope to do it in pure java, and on runntime. "A callback function can be created on runtime, without C/C++ compiler" is my request.

Using LLVM is also an good option. I think it is a little bit overkill or too heavy? Since you may want to support android.

@Neiko2002 out of curiosity, what do you want to achieve? Run C directly in Java at runtime and interactive with Java environment? Or even write Java wrapper with JavaCPP, translate it into C and then make things in the last sentence happen?

I think this gonna be a really cool feature. Doing it well makes JavaCPP have numba feature.

However for now, as a user I just need something like python's ctypes.

Some personal thoughts: a fundamental features of these kind of language binding infrasturcture is to provide the glue language, and python did it well. For high performance part, leave it to C, or as you suggested through LLVM.

@saudet
Copy link
Member

saudet commented Aug 2, 2020

Are you guys talking about a main() function for LLVMOrcGetSymbolAddress()? That's only a single function definition, which gets reused for all calls to any other function, so that's pretty easy to add in a new header file in here:
https://github.com/bytedeco/javacpp-presets/tree/master/llvm/src/main/resources/org/bytedeco/llvm/include

In any case, I've bundled the clang program in the latest commit and that works just fine:

String clang = Loader.load(org.bytedeco.llvm.program.clang.class);
ProcessBuilder pb = new ProcessBuilder(clang, "--version");
pb.inheritIO().start().waitFor();

@oneengineer
Copy link
Author

Yes, that's what I am talk about.
This is good example
LLVM-C ORC JIT

@Neiko2002
Copy link
Member

Actually, LLVMOrcAddEagerlyCompiledIR is the reason I ask for "be able to provide callback function in pure java".
Yes there is no default (as far as I know) callback function for LLVMOrcAddEagerlyCompiledIR in provided LLVM C-API.

I think callback functions work in general but those LLVMOrcAddEagerlyCompiledIR or LLVMOrcAddLazilyCompiledIR function never call them. Here is a small repo where I tested three different ways of compiling with ORC. There is a version where I use a callback function LLVMOrcLazyCompileCallbackFn with a Java implementation and this callback function gets perfectly called by LLVMOrcCreateLazyCompileCallback.

But then I have a version with LLVMOrcAddLazilyCompiledIR which produces an invokable LLVM function but the LLVMOrcSymbolResolverFn won't get called. At last, I tried LLVMOrcAddEagerlyCompiledIR and here again LLVMOrcSymbolResolverFn does not get called and it does not even produce executable machine code.

So right now ORC compilation does only work with LLVMOrcAddLazilyCompiledIR.

@Neiko2002 out of curiosity, what do you want to achieve? Run C directly in Java at runtime and interactive with Java > environment? Or even write Java wrapper with JavaCPP, translate it into C and then make things in the last sentence happen?

I just wanted to produce and run LLVM code in Java at runtime. Thanks to @saudet I now can even write C/C++ code, translate it to LLVM IR and run it.

@oneengineer
Copy link
Author

@Neiko2002

Your example reminds me that I can pass multiple parameter even with MCJIT compiled function, with LLVMGetFunctionAddress:

        long addr = LLVMGetFunctionAddress(engine, "aplusb");
        var pointer = com.sun.jna.Pointer.createConstant( addr );
        var function = com.sun.jna.Function.getFunction(pointer);
        Object r = function.invoke( int.class, new Object[] { 10, 23} );
        int temp = (Integer)r;
        System.out.println("result is " + temp); // 33

This is what I was looking for at the first place!

Thanks a lot!

BTW, does anyone what's the purpose of LLVMGenericValue? Is it only used in LLVMRunFunction?
And how do I map java JNA type to LLVM type, e.g. how do I pass i1 to a JITed LLVM function?

@Neiko2002
Copy link
Member

@oneengineer please take a look at my new library even if you do not plan to use the Polly compiler there, just use the LLVMProgram class and its two dependency classes. This way your native function calls are type-safe.

@oneengineer
Copy link
Author

I see, you implemented the check at checkLLVMTypeCompatibility.
How did you handle bool? I didn't see such logic.
That is to say int b = LLVM.LLVMGetIntTypeWidth(llvmType); b can be 1.

@Neiko2002
Copy link
Member

You are right. I have added it. The conversion between the Java data types and native data types is handled by JNR.

@saudet
Copy link
Member

saudet commented May 20, 2021

FYI, I've added presets for libffi in commit 3075f99, so we can now use that directly at least to avoid any overhead from JNA or JNR.

/cc @yukoba @supergrecko

@junlarsen
Copy link
Member

FYI, I've added presets for libffi in commit 3075f99, so we can now use that directly at least to avoid any overhead from JNA or JNR.

That's good to hear, will take a closer look at this. Thank you

@junlarsen
Copy link
Member

FYI, I've added presets for libffi in commit 3075f99, so we can now use that directly at least to avoid any overhead from JNA or JNR.

That's good to hear, will take a closer look at this. Thank you

Updated the gcc preset samples in bytedeco/gcc@b265a18 - Will also add a couple examples for the LLVM preset.

@saudet
Copy link
Member

saudet commented Aug 3, 2021

The presets for libffi have been released with version 1.5.6. Enjoy!
https://github.com/bytedeco/javacpp-presets/tree/master/libffi

@saudet saudet closed this as completed Aug 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants