jextract C++ support
Jorn Vernee
jorn.vernee at oracle.com
Sun Sep 24 21:33:41 UTC 2023
Hi Rel,
> 1. Am I doing something wrong in C++ test so that it crashes?
I can't say for sure without seeing the struct's definition.
Note though that, there are many cases in which the C and C++ ABIs
differ. At least for the moment, the FFM linker only supports linking
using the C ABI, so in order to reliably call C++, you would have to
define the C++ functions with C linkage (using `extern "C"`).
> Is it done in purpose or I am looking at a wrong documentation and
usage of SegmentAllocator in invokeExact described somewhere else?
The documentation of invokeExact is very general, and doesn't cover the
FFM's use of method handles in particular. You can find a description of
the additional SegmentAllocator parameter in the documentation of
Linker::downcallHandle [1]. E.g. "... if the function descriptor's
return layout is a group layout, the resulting downcall method handle
accepts an additional leading parameter of type SegmentAllocator, which
is used by the linker runtime to allocate the memory region associated
with the struct returned by the downcall method handle. "
> 3. Any ideas on how rust-bindgen deals with RVO?
As far as I know, rust-bindgen only generations functions with C
linkage, so RVO is not in the picture.
Jorn
[1]:
https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/foreign/Linker.html#downcallHandle(java.lang.foreign.FunctionDescriptor,java.lang.foreign.Linker.Option...)
On 24/09/2023 20:10, Rel wrote:
> Hi all,
>
> Currently my main focus is to support:
>
> std::string helloWorld();
>
> I managed to generate "some" layout for std::string but I encountered
> problems of a different level :)
>
> Let's take more simple examples where function will return object and
> do it for C first:
>
> struct B returnStruct(); // C
>
> My test looks like:
>
> var alloc =
> SegmentAllocator.slicingAllocator(Arena.ofAuto().allocate(B.sizeof()));
> assertEquals(10,
> A.a$get(B.a$slice(Libcexperiments.returnStruct(alloc))));
>
> It passes.
>
> Now C++ test:
>
> Point2d createPoint2d(); // C++
>
> The way C++ returns objects (struct/class) is not very similar to C.
> It highly depends on C++ RVO
> (https://sigcpp.github.io/2020/06/08/return-value-optimization).
> Currently I try to disable it by compiling libcppexperiments with
> -fno-elide-constructors and call copy ctors which is generated by the
> compiler
>
> My test looks like:
>
> var mem = Arena.global().allocate(Point2d.sizeof());
> printDump(mem.asByteBuffer());
> var pointAlloc = SegmentAllocator.slicingAllocator(mem);
> var pointPtr2 = Happy.createPoint2d(pointAlloc);
> printDump(mem.asByteBuffer());
>
> The result is:
>
> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> happy::Point::Point(this = 0x7fbc2c533f70)
> happy::Point2d::Point2d(20, 22)
> 7fbc2c533f70
> 30 cd 04 30 bc 7f 00 00 14 00 00 00 16 00 00 00
> [x=20,y=22]
> 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
> happy::createPoint2d
> happy::Point::Point(this = 0x7fbc33dbddd0)
> happy::Point2d::Point2d(2, 3)
> #
> # A fatal error has been detected by the Java Runtime Environment:
> #
> # SIGSEGV (0xb) at pc=0x00007fbc30049bf9, pid=2704200, tid=2704208
> #
> # JRE version: OpenJDK Runtime Environment (22.0+12) (build 22-ea+12-877)
> # Java VM: OpenJDK 64-Bit Server VM (22-ea+12-877, mixed mode,
> sharing, tiered, compressed oops, compressed class ptrs, g1 gc,
> linux-amd64)
> # Problematic frame:
> # C [libcppexperiments.so+0x2bf9] happy::Point::Point(happy::Point
> const&)+0x1f
>
> I have two questions:
>
> 1.
> Am I doing something wrong in C++ test so that it crashes?
>
> 2.
> It seems that C++ tries to copy data to memory which suppose to be
> provided by pointAlloc
>
> When jextract (prod version) generates code for such example in C, it
> expects users to define SegmentAllocator and it passes it to
> MethodHandle::invokeExact. The documentation of invokeExact is vague
> and it says that:
>
> > args - the signature-polymorphic parameter list, statically represented using varargs
>
> and does not cover usecase that args may contain SegmentAllocator (I
> guess it is to allocate memory where returned object will we saved?).
> Is it done in purpose or I am looking at a wrong documentation and
> usage of SegmentAllocator in invokeExact described somewhere else?
>
> 3.
> Any ideas on how rust-bindgen deals with RVO?
>
> Regards,
> ------- Original Message -------
> On Wednesday, September 6th, 2023 at 12:34 AM, Rel <enatai at proton.me>
> wrote:
>
>> Short updates from me.
>>
>> I continue working on generating layout for std::string and found
>> that it is failing on cxx branch:
>>
>> Scoped: TOPLEVEL <toplevel>
>> Scoped: STRUCT __mbstate_t layout =
>> [i32(__count)[i32(__wch)|[4:b8](__wchb)](__value)](__mbstate_t)
>> Attr: LINK -> []
>> Variable: FIELD __count type = Int(layout = i32)
>> Variable: FIELD __value type =
>> Declared([i32(__wch)|[4:b8](__wchb)](union (unnamed at enum.h:28:3)))
>>
>> java.lang.IllegalArgumentException: Invalid member name: union
>> (unnamed at enum.h:28:3)
>> at
>> java.base/java.lang.constant.ConstantUtils.validateMemberName(ConstantUtils.java:152)
>> at java.base/java.lang.constant.ClassDesc.of(ClassDesc.java:129)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.ClassSourceBuilder.<init>(ClassSourceBuilder.java:70)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.StructBuilder.<init>(StructBuilder.java:67)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.StructBuilder.<init>(StructBuilder.java:62)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.StructBuilder.addStruct(StructBuilder.java:136)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.OutputFactory.visitScoped(OutputFactory.java:187)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.OutputFactory.visitScoped(OutputFactory.java:54)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.DeclarationImpl$ScopedImpl.accept(DeclarationImpl.java:333)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.OutputFactory.visitVariable(OutputFactory.java:354)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.OutputFactory.visitVariable(OutputFactory.java:54)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.DeclarationImpl$VariableImpl.accept(DeclarationImpl.java:172)
>> at
>> org.openjdk.jextract at 21/org.openjdk.jextract.impl.OutputFactory.lambda$visitScoped$0(OutputFactory.java:201)
>>
>> But same worked with latest changes in panama branch.
>>
>> In order to update cxx branch to panama branch I decided to use
>> rebase instead of merge (I see Maurizio was merging them instead).
>> The reason why I did rebase is that way our cpp changes will be
>> always on top of whatever changes were done in panama branch (and not
>> spread across the branch) so it will be easy to track them.
>>
>> Initially we had following branch:
>> cxx - Maurizio initial cpp support
>>
>> Now I added cxx2 branch which is rebase on latest panama branch with
>> Maurizio changes.
>>
>> Now, if you remember previously we did mangle only for member
>> functions, and for ordinary functions we had define wrapper manually
>> like:
>>
>> https://github.com/enatai/panamaexperiments/blob/ec76f9c2d25734a8c9f93f6b4200161b353c81f0/cppexperiments/src/main/java/libcppexperiments/CalcDistance.java
>>
>> I fixed it so jextract mangles, in case of C++, non member functions too:
>>
>> https://github.com/enatai/jextractcpp/commit/f1f64e31702b06aa9325546013758ce912f36363
>>
>> I plan to continue to do my changes in cxx2 branch and keep rebasing
>> it to panama branch once in a while
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/jextract-dev/attachments/20230924/17108fb2/attachment-0001.htm>
More information about the jextract-dev
mailing list