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

Expose the Julia JIT with a C API #49858

Merged
merged 5 commits into from
May 29, 2023
Merged

Conversation

gbaraldi
Copy link
Member

@gbaraldi gbaraldi commented May 17, 2023

This is a proof of concept to expose the Julia JIT to foreign users. The idea is to use this interface to solve #49056.

For now it only half works
Before

julia> call_delayed(foo, 3)
ERROR: heyy
Stacktrace:
 [1] macro expansion
   @ ./REPL[19]:337 [inlined]
 [2] abi_call
   @ ./REPL[19]:261 [inlined]
 [3] call_delayed(f::typeof(foo), args::Int64)
   @ Main.LazyCodegen ./REPL[19]:347
 [4] top-level scope
   @ REPL[58]:1

With this PR + the LLVM.jl changes JuliaLLVM/LLVM.jl#346

julia> call_delayed(foo, 3)
ERROR: heyy
Stacktrace:
 [1] foo
   @ ./REPL[16]:3
 [2] macro expansion
   @ Main.LazyCodegen ./REPL[14]:325 [inlined]
 [3] abi_call(f::Ptr{Nothing}, rt::Type{Int64}, tt::Type{Tuple{Int64}}, func::typeof(foo), args::Int64)
   @ Main.LazyCodegen ./REPL[14]:249 [inlined]
 [4] call_delayed(f::typeof(foo), args::Int64)
   @ Main.LazyCodegen ./REPL[14]:335
 [5] top-level scope
   @ REPL[17]:1

This still misses some traces, but it's a start.

I want to move those functions to a separate file and maybe some of them to LLVMExtra

Apparently it was missing debug info. Starting julia with -g2 lets us get the full trace (with no details because of no code/method_instances)

julia> declared_bar(3)
ERROR: heyehye
Stacktrace:
 [1] error
   @ ./error.jl:35
 [2] foo
   @ ./REPL[13]:3
 [3] bar
   @ ./REPL[22]:1
 [4] declared_bar(x::Int64)
   @ Main ./REPL[31]:2
 [5] top-level scope
   @ REPL[32]:1

@vchuravy vchuravy requested review from pchintalapudi and maleadt May 17, 2023 18:50
@pchintalapudi
Copy link
Member

Is there a reason to allow people to add LLVM IR to an IRCompileLayer instead of just requiring them to compile to an object file on their own?

@gbaraldi
Copy link
Member Author

I added in order to have something to call emit on. It's probably not super necessary.

@gbaraldi gbaraldi marked this pull request as ready for review May 19, 2023 20:34
JL_DLLEXPORT_CODEGEN const char *
LLVMExtraJLJITGetDataLayoutString_impl(JuliaOJITRef JIT)
{
return unwrap(JIT)->getDataLayout().getStringRepresentation().c_str();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The C API supports DataLayout objects, so just return it directly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't know that, because the other functions (GetDataLayout,SetDatalayout etc) that move it around all use C strings

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I see that the existing APIs use strings too (LLVMOrcLLJITGetDataLayoutStr), so I'm OK keeping it as strings for consistency.

@maleadt
Copy link
Member

maleadt commented May 22, 2023

I'm not familiar with ORC or our IR compiler layer, so I'll defer to @vchuravy and @pchintalapudi.

@@ -1289,6 +1289,7 @@ JuliaOJIT::JuliaOJIT()
ES(cantFail(orc::SelfExecutorProcessControl::Create())),
GlobalJD(ES.createBareJITDylib("JuliaGlobals")),
JD(ES.createBareJITDylib("JuliaOJIT")),
ExternalJD(ES.createBareJITDylib("JuliaExternal")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Long-term we might want many ExternalJDs and was considering a design where we had one JD per world in Julia.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can add as many JDs as we want with the ExecutionSession. The difference is that this one is linked in by default.

@vchuravy vchuravy merged commit 957972e into JuliaLang:master May 29, 2023
@@ -1385,6 +1388,9 @@ JuliaOJIT::JuliaOJIT()
}

JD.addToLinkOrder(GlobalJD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly);
JD.addToLinkOrder(ExternalJD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly);
ExternalJD.addToLinkOrder(GlobalJD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly);
ExternalJD.addToLinkOrder(JD, orc::JITDylibLookupFlags::MatchExportedSymbolsOnly);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should not be any way for ExternalJD to access JD or vice versa. Neither of those should be accidentally breaking the content of the other.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this one is kind of wrong, but we do want to keep the ability to llvmcall a function from an external JD to avoid doing the trampoline/function pointer dance we do right now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems likely to go very badly for users. The llvmcall itself right now should not be able to access JD state either, but that is a different bug.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would it go bad for the users? (llvmcall currently aborts if it doesn't find a symbol but that's separate)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JD is an implementation detail and not one that is stable, or one that we think is good right now. We could fix the llvmcall crash by checking that it does not contain declarations (except of intrinsic things) and otherwise reject it when we parsed it. That would do the right thing.

Comment on lines +1519 to +1520
orc::JITDylib* SearchOrders[3] = {&JD, &GlobalJD, &ExternalJD};
ArrayRef<orc::JITDylib*> SearchOrder = makeArrayRef(&SearchOrders[0], ExportedSymbolsOnly ? 3 : 1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change should be reverted?

Comment on lines +1532 to +1534
Expected<JITEvaluatedSymbol> JuliaOJIT::findExternalJDSymbol(StringRef Name, bool ExternalJDOnly)
{
orc::JITDylib* SearchOrders[3] = {&ExternalJD, &GlobalJD, &JD};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should take some sort of handle that was returned by from addExternalModule / addObjectFile to do the lookup inside of. No global variables (esp. GlobalJD and JD) should be accessed here since their content is unreliable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants