mono / CppSharp

Tools and libraries to glue C/C++ APIs to high-level languages
MIT License
3.12k stars 513 forks source link

arm64 support #1734

Open hxbb00 opened 1 year ago

hxbb00 commented 1 year ago

 I am trying to build CppSharp for arm64, but i do not know how to generate bindings(CppSharp/src/CppParser/Bindings/CSharp/aarch64-linux-gnu-cxx11abi), can anyone help me?

tritao commented 1 year ago

Hey, those parser bindings are generated by running the ParserGen tool: https://github.com/mono/CppSharp/blob/main/src/CppParser/ParserGen/ParserGen.cs

You need to extend the parser generation setup in that file to add the triple you want to generate for.

You may also need this zip: https://github.com/mono/CppSharp/releases/download/CppSharp/headers.zip

Are you running CppSharp on an ARM64 system itself or trying to cross-generate?

hxbb00 commented 1 year ago

I have almost finished the ARM64 support work, but there is a problem, I don't know whether it is a bug in the running time, can you help me to analyze the reason, here the pseudocode:

    // DEBUG: class DLL_API StructWithPrivateFields
    // DEBUG: {
    // DEBUG: public:
    // DEBUG:     StructWithPrivateFields(int simplePrivateField, Foo complexPrivateField);
    // DEBUG:     int getSimplePrivateField();
    // DEBUG:     Foo getComplexPrivateField(); // ======the problem is here
    // DEBUG: protected:
    // DEBUG:     int protectedField;
    // DEBUG: private:
    // DEBUG:     int simplePrivateField;
    // DEBUG:     Foo complexPrivateField;
    // DEBUG: }

When the function return value is a structure, the p/invoke function signature looks like this under the x86_64:

[SuppressUnmanagedCodeSecurity, DllImport("ConsoleApplicationARM"
                , EntryPoint = "_ZN23StructWithPrivateFields22getComplexPrivateFieldEv", CallingConvention = __CallingConvention.Cdecl)]
            internal static extern void GetComplexPrivateField_1(__IntPtr @return, __IntPtr __instance);

Pass the return value store address before(Linux) or after(MSVC) 'this' parameter(View bindings code ),But under the Arm64 architecture, it doesn't work,

tritao commented 1 year ago

I was checking and Clang uses the generic Itanium ABI for AArch64: https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/ItaniumCXXABI.cpp#L517

So I would expect the bindings should look exactly the same as on Linux x64 for these indirect returns.

I would try a simple example to trigger this in C++, look at the LLVM IR emitted by Clang to make sure it's what I expect, then try to P/Invoke it manually with a simple C# example. That should clear things up a bit.

hxbb00 commented 1 year ago

I was checking and Clang uses the generic Itanium ABI for AArch64: https://github.com/llvm/llvm-project/blob/main/clang/lib/CodeGen/ItaniumCXXABI.cpp#L517

So I would expect the bindings should look exactly the same as on Linux x64 for these indirect returns.

I would try a simple example to trigger this in C++, look at the LLVM IR emitted by Clang to make sure it's what I expect, then try to P/Invoke it manually with a simple C# example. That should clear things up a bit.

I also think so at first, but it seems that the result is not so. Looking forward to your verification result

tritao commented 1 year ago
struct  Foo
{
    int A;
    int P1;
    int P2;
    int P3;
    int P4;
};

Foo getComplexPrivateField() { return Foo(); }

int main()
{
    return getComplexPrivateField().A;
}

ARM64 LLVM IR:

%struct.Foo = type { i32, i32, i32, i32, i32 }

define dso_local void @_Z22getComplexPrivateFieldv(ptr noalias sret(%struct.Foo) align 4 %0) #0 !dbg !10 {
  call void @llvm.memset.p0.i64(ptr align 4 %0, i8 0, i64 20, i1 false), !dbg !23
  ret void, !dbg !24
}

declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #1

define dso_local noundef i32 @main() #2 !dbg !25 {
  %1 = alloca i32, align 4
  %2 = alloca %struct.Foo, align 4
  store i32 0, ptr %1, align 4
  call void @_Z22getComplexPrivateFieldv(ptr sret(%struct.Foo) align 4 %2), !dbg !28
  %3 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0, !dbg !29
  %4 = load i32, ptr %3, align 4, !dbg !29
  ret i32 %4, !dbg !30
}

x86_64 LLVM IR:

%struct.Foo = type { i32, i32, i32, i32, i32 }

define dso_local void @_Z22getComplexPrivateFieldv(ptr noalias sret(%struct.Foo) align 4 %0) #0 !dbg !8 {
  call void @llvm.memset.p0.i64(ptr align 4 %0, i8 0, i64 20, i1 false), !dbg !21
  ret void, !dbg !22
}

declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) #1

define dso_local noundef i32 @main() #2 !dbg !23 {
  %1 = alloca i32, align 4
  %2 = alloca %struct.Foo, align 4
  store i32 0, ptr %1, align 4
  call void @_Z22getComplexPrivateFieldv(ptr sret(%struct.Foo) align 4 %2), !dbg !26
  %3 = getelementptr inbounds %struct.Foo, ptr %2, i32 0, i32 0, !dbg !27
  %4 = load i32, ptr %3, align 4, !dbg !27
  ret i32 %4, !dbg !28
}

As predicted, ABI looks the same, @_Z22getComplexPrivateFieldv(ptr sret(%struct.Foo) align 4 %2) signature looks the same, and sret parameter as expected.

Reproducable in: https://godbolt.org/z/qcqjsK4PM

Note: I simplified the code to the bare mininum, but with a this member function it's the same: https://godbolt.org/z/7Trjfq4qr

hxbb00 commented 1 year ago

Is it possible to have a problem with the p/invoke stub

tritao commented 1 year ago

Is it possible to have a problem with the p/invoke stub

If you want more help, you need to put more information in your responses. Or send a PR with your changes so I can take a look.

hxbb00 commented 1 year ago

ConsoleApplicationARMTest.zip I'm sorry, here my sample code, i run it on my arm64 machine, The results are as follows: image image

hxbb00 commented 1 year ago

Hi @tritao !Did you reproduce the problem?or do you need any more information from me

wegylexy commented 1 year ago

What about ARM64EC for Windows 11 on ARM?

tritao commented 1 year ago

What about ARM64EC for Windows 11 on ARM?

This is not supported currently, not even sure if Clang already supports this one.