Cxx.jl icon indicating copy to clipboard operation
Cxx.jl copied to clipboard

RFC: Julia 1.7 support

Open Gnimuc opened this issue 3 years ago • 22 comments

As reworking Cxx.jl from the scratch takes more time than I thought, I'm going to try it again to upgrade the old code. Thanks to https://github.com/JuliaLang/llvm-project, doing a source build is much easier than before.

  • [x] Switch the build system to CMake (the C++ code is temporarily hosted here)
  • [ ] libcxxffi
    • [x] Upgrade libcxxffi to LLVM 12
    • [x] Find the reason why cleanup_cpp_env segfaults
    • [ ] Implement exception-handing for Windows (x-ref) (pending)
    • [ ] Fix the “Duplicate definition of symbol” issue by force-applying linkage
      • [x] private GVs
      • [ ] global GVs (needs help)
  • [ ] Cxx.jl
    • [x] Regenerate clang_constants.jl
    • [x] Refactor initialization.jl(use ClangCompiler.jl's JLLEnvs module for shipping system headers)
    • [x] Adapt llvmcall to Julia 1.7
    • [x] Rework IR generation on top of LLVM.jl
  • [x] Find the reason why modules can not be correctly merged.
  • [ ] Broken tests:
    • [ ] cxxstr.jl: 103
    • [ ] icxxstr.jl: inlineexpr, 103
    • [ ] misc.jl
      • [ ] 53
      • [ ] 55
      • [ ] booleans as template arguments
      • [ ] Members with non-trivial copy constructor
      • [ ] Memory management
      • [ ] 141
      • [ ] Implement C++ Functions in julia
      • [ ] fooTheLambda
      • [ ] 217
      • [ ] 243
      • [ ] 303

Gnimuc avatar Feb 17 '22 16:02 Gnimuc

Thanks for this @Gnimuc , having Cxx back will be awesome!

oschulz avatar Feb 17 '22 16:02 oschulz

I'm going to try it again to upgrade the old code.

Great! Is this a temporary solution, i.e. likely to break again in 1.8 (that seems real close), or later Julia version (or strictly speaking major LLVM version upgrades, which I think was the problem)? I'm not up-to-speed on the reimplementation, or this update, but am curious why target 1.7, not 1.6? That's good enough for me, and I guess many others, or even 1.8 if it helps. Well I'm not a Cxx user, I just like this package, and would promote it (again) if I think it's not likely to break again.

Feel free to close/"won't fix" #487 (unless you later plan to backport to 1.6) that I opened (and Spot.jl mentioned there, already moved to CxxWrap.jl), and I suppose all other issues on 1.4, 1.5...

PallHaraldsson avatar Feb 25 '22 10:02 PallHaraldsson

Is this a temporary solution, i.e. likely to break again in 1.8 (that seems real close), or later Julia version (or strictly speaking major LLVM version upgrades, which I think was the problem)?

I'm sure it will break again in 1.8, but it should be easier to upgrade. The main problem here is that Cxx.jl uses some Julia internal functions(e.g. jl_get_llvmf_decl) which were removed between Julia 1.4~1.5, so the corresponding Cxx features (e.g. interpolating a Julia function call expr in icxx_str macro) need to be adapted accordingly.

There is no intention to resurrect all of the cool features of Cxx.jl in this PR, I just want to see how far it goes and what Julia internal functions we need, so we can open feature requests in the Julia upstream to add new stable APIs for us.

I'm not up-to-speed on the reimplementation, or this update, but am curious why target 1.7, not 1.6?

For the reimplementation, I'd like to use the newly introduced incremental compiling feature in LLVM 13, but that will need Julia 1.8. For this upgrade, we don't have the patched source code of LLVM/Clang for Julia 1.6 at this moment(we will never have that if no one would like/has time to contribute).

Gnimuc avatar Feb 26 '22 07:02 Gnimuc

I like your plan, @Gnimuc - again, thanks for trying to resurrect Cxx!

oschulz avatar Feb 26 '22 07:02 oschulz

Now, I can get the following examples to work. Unfortunately, due to some changes in Julia's JIT, it's not easy to resurrect the magic "insert Julia functions into the C++ code" feature(e.g. Example 4 in the README). Any feedback on what features are used in your old Cxx.jl-based projects?

# runs on macOS with Julia 1.7.2
julia> using Cxx

julia> cxx""" #include<iostream> """
true

julia> cxx"""  
         void mycppfunction() {   
            int z = 0;
            int y = 5;
            int x = 10;
            z = x*y + 2;
            std::cout << "The number is " << z << std::endl;
         }
       """
true

julia> julia_function() = @cxx mycppfunction()
julia_function (generic function with 1 method)

julia> julia_function()
In file included from /Cxx.h:1:
/Users/gnimuc/.julia/dev/Cxx/src/std.jl:112:38: warning: expression result unused
std::vector<bool> &vr = __juliavar1; vr;
                                     ^~
/Users/gnimuc/.julia/dev/Cxx/src/std.jl:43:12: error: implicit instantiation of undefined template 'std::vector<bool>'
__juliavar1.size();
           ^
/Users/gnimuc/.julia/artifacts/8571aac530057acc3f32ffab9003dc3e00b24723/x86_64-apple-darwin14/sys-root/usr/include/c++/v1/iosfwd:217:28: note: template is declared here
class _LIBCPP_TEMPLATE_VIS vector;
                           ^
In file included from /Cxx.h:1:
/Users/gnimuc/.julia/dev/Cxx/src/std.jl:112:38: warning: expression result unused
std::vector<bool> &vr = __juliavar1; vr;
                                     ^~
The number is 52

julia> jnum = 10
10

julia> cxx"""
           void printme(int x) {
              std::cout << x << std::endl;
           }
       """
true

julia> @cxx printme(jnum)
10

julia> cxx"""
         void printme(const char *name) {
            // const char* => std::string
            std::string sname = name;
            // print it out
            std::cout << sname << std::endl;
         }
            """
true

julia> @cxx printme(pointer("John"))
John

julia> cxx"""
       class Klassy {
           public:
               enum Foo { Bar, Baz };
               static Foo exec(Foo x) { return x; }
       };
       """
true

julia> @cxx Klassy::Bar
Cxx.CxxCore.CppEnum{Symbol("Klassy::Foo"), UInt32}(0x00000000)

julia> @cxx Klassy::exec(@cxx(Klassy::Baz))
Cxx.CxxCore.CppEnum{Symbol("Klassy::Foo"), UInt32}(0x00000001)

julia> cxx"""#include <iostream>
       class Hello
       {
           public:
               void hello_world(const char *now){
                   std::string snow = now;
                   std::cout << "Hello World! Now is " << snow << std::endl;
               }
        };"""
true

julia> hello_class = @cxxnew Hello()
(class Hello *) @0x00006000013d5040

julia> using Dates

julia> tstamp = string(Dates.now())
"2022-03-31T01:01:02.900"

julia> @cxx hello_class -> hello_world(pointer(tstamp))
Hello World! Now is 2022-03-31T01:01:02.900

Gnimuc avatar Mar 30 '22 16:03 Gnimuc

For those contributors who would like to get involved in the development of this package, this PR is probably a good starting point. To test this PR, you need to 1. build https://github.com/Gnimuc/libcxxffi and make install; 2. add export LIBCXXFFI_INSTALL_PREFIX=/Users/your/path/to/libcxxffi/build/install to your .bashrc; 3. dev Cxx.jl (you may also need to install https://github.com/Gnimuc/LLVMCGUtils.jl).

Gnimuc avatar Mar 30 '22 16:03 Gnimuc

EDIT: NVM just noticed a submodule :/

ShoofLLC avatar Apr 02 '22 11:04 ShoofLLC

We've built & installed libcxxffi, exported the path, added LLVMCGUtils.jl, and tried to build Cxx.jl using Pkg.add(url="https://github.com/JuliaInterop/Cxx.jl.git", rev="fix-julia-1.7"), but we've got an error collectAllHeaders not defined (the function definition has been removed from initialization.jl in this PR)

julia> using Cxx
[ Info: Precompiling Cxx [a0b5b9ef-44b7-5148-a2d1-f6db19f3c3d2]
ERROR: LoadError: UndefVarError: collectAllHeaders not defined
Stacktrace:
 [1] top-level scope
   @ ~/.julia/packages/Cxx/cr4BV/src/initialization.jl:232
 [2] include(x::String)
   @ Cxx.CxxCore ~/.julia/packages/Cxx/cr4BV/src/Cxx.jl:143
 [3] top-level scope
   @ ~/.julia/packages/Cxx/cr4BV/src/Cxx.jl:173
 [4] top-level scope (repeats 2 times)
   @ none:1
in expression starting at /home/strazce/.julia/packages/Cxx/cr4BV/src/initialization.jl:232
in expression starting at /home/strazce/.julia/packages/Cxx/cr4BV/src/Cxx.jl:141
ERROR: Failed to precompile Cxx [a0b5b9ef-44b7-5148-a2d1-f6db19f3c3d2] to /home/strazce/.julia/compiled/v1.7/Cxx/jl_w17t2p.

sliwowitz avatar Jun 02 '22 14:06 sliwowitz

I had a quick test on linux. It managed to compile C++ code, but crashes when the llvmcall is compiled. I can not procede any further so I leave a quick note here in case someone has time.

but we've got an error collectAllHeaders not defined

  • Simply comment it out and leave it as String[] seems to be fine.

  • Running with assertions on discovered that https://github.com/Gnimuc/libcxxffi/blob/ea73ce6b2b1173ad40528a2878d4ddacdb7a707b/lib/libcxxffi.cpp#L1465 made @llvm.global_ctors' linkage from appending to external. (https://github.com/Gnimuc/libcxxffi/pull/1)

  • The generator in @cxx, namely Cxx.CxxCore._cppcall(Cxx.CxxCore.CxxInstance{1}, (Cxx.CxxCore.CppNNS){(Tuple){:mycppfunction}}, false, false, ()) returns an llvmcall. Compiling this llvmcall triggers an assertion error because RTDyldMemoryManagerJL::allocateCodeSection refuses to allocate multiple code sections. IIRC Julia on macOS uses JITLink instead of RTDyld, so this code path may be different from macOS'. (https://github.com/Gnimuc/libcxxffi/pull/2)

melonedo avatar Oct 02 '22 12:10 melonedo

Now I am stuck again. It is forbidden to call eval inside a generated function, so we need to find another way to call the constructors.

@Gnimuc have you come across similar issues about constructors? It seems that related functions (RunGlobalConstructors) are commented out.

melonedo avatar Oct 04 '22 14:10 melonedo

the function RunGlobalConstructors is implemented in LLVMCGUtils.jl

令和4年10月4日(火) 23:31 melonedo @.***>:

Now I am stuck again. It is forbidden to call eval inside a generated function, so we need to find another way to call the constructors.

@Gnimuc https://github.com/Gnimuc have you come across similar issues about constructors? It seems that related functions (RunGlobalConstructors) are commented out.

— Reply to this email directly, view it on GitHub https://github.com/JuliaInterop/Cxx.jl/pull/496#issuecomment-1267098904, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABYXW5MFDQ5ZKQUCA6JA3SDWBQ5S7ANCNFSM5OVFOKUQ . You are receiving this because you were mentioned.Message ID: @.***>

Gnimuc avatar Oct 04 '22 14:10 Gnimuc

I am sorry but I can not find RunGlobalConstructors in https://github.com/Gnimuc/LLVMCGUtils.jl, can you provide more information?

melonedo avatar Oct 04 '22 15:10 melonedo

I am sorry but I can not find RunGlobalConstructors in https://github.com/Gnimuc/LLVMCGUtils.jl, can you provide more information?

Sorry, I was using a phone last night and didn't check the code. What I meant is that the CollectGlobalConstructors is implemented in LLVMCGUtils.jl.

@Gnimuc have you come across similar issues about constructors? It seems that related functions (RunGlobalConstructors) are commented out.

Indeed. RunGlobalConstructors are commented out in this PR. But I forget why I did that. 😰 The reason could be either I mistakenly did that to debug something or calling those global constructors manually is no longer necessary(I doubt this is valid though). Did you run into any problems related to global ctors?

To call CollectGlobalConstructors, we could use LLVM.jl's call_function rather than eval.

Gnimuc avatar Oct 05 '22 00:10 Gnimuc

Indeed. RunGlobalConstructors are commented out in this PR. But I forget why I did that. 😰

I guess it is because its signature changed. It accepts an LLVM.Module, but a CxxInstance is passed. It seems that the constructor issue is because I only tested the llvmcall alone, running it as @cxx mycppfunction seems to be fine.

Now mycppfunction can be compiled and run, but printme fails because of "Duplicate definition of symbol std::__ioinit".

melonedo avatar Oct 05 '22 02:10 melonedo

"Duplicate definition of symbol std::__ioinit"

This is exactly where I got stuck.

Gnimuc avatar Oct 05 '22 11:10 Gnimuc