Still WIP, based on the Kaleidoscope/BuildingAJIT/Chapter4.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
@lhames this currently fails with
➜ build git:(vc/orcv2-lazy-capi) ✗ bin/OrcV2CBindingsVeryLazy 0 Error: Duplicate definition of symbol 'foo_body' fish: Job 1, 'bin/OrcV2CBindingsVeryLazy 0' terminated by signal SIGSEGV (Address boundary error)
Should I have used independent JITDylibs or done a triple indirection foo -> foo_trampoline -> foo_body?
I see a couple of warnings when building this code:
/path/to/llvm-project/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c:127:14: warning: variable 'TSM' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized] } else if (strcmp(CSym, "bar_body") == 0) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /path/to/llvm-project/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c:133:10: note: uninitialized use occurs here assert(TSM); ^~~ /Library/Developer/CommandLineTools/SDKs/MacOSX11.5.sdk/usr/include/assert.h:93:25: note: expanded from macro 'assert' (__builtin_expect(!(e), 0) ? __assert_rtn(__func__, __FILE__, __LINE__, #e) : (void)0) ^ /path/to/llvm-project/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c:127:10: note: remove the 'if' if its condition is always true } else if (strcmp(CSym, "bar_body") == 0) { ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /path/to/llvm-project/llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c:120:33: note: initialize the variable 'TSM' to silence this warning LLVMOrcThreadSafeModuleRef TSM; ^ = NULL
and it asserts on execution on Darwin with:
./bin/OrcV2CBindingsVeryLazy Assertion failed: ((!RequiresNullTerminator || BufEnd[0] == 0) && "Buffer is not null terminated!"), function init, file /path/to/llvm-project/llvm/lib/Support/MemoryBuffer.cpp, line 49.
I'll see if I can figure out what's going on there tomorrow.
Regarding the MaterializationResponsibility API: it would be good to add the addDependencies and addDependenciesForAll methods. These declare dependencies for each materializing symbol so that ORC can track them in its dependence graph, which in turn ensures that lookups remain safe regardless of what threads the symbols are being materialized on. These methods are an essential part of the MaterializationResponsibility API.
llvm/include/llvm-c/Orc.h | ||
---|---|---|
725–741 | This can be removed for now, since the operation isn't supported in the C++ API yet. | |
llvm/lib/ExecutionEngine/Orc/OrcV2CBindings.cpp | ||
505–514 | I'll remove this function on the mainline. This code can be dropped from this review. |
Regarding the MaterializationResponsibility API: it would be good to add the addDependencies and addDependenciesForAll methods. These declare dependencies for each materializing symbol so that ORC can track them in its dependence graph, which in turn ensures that lookups remain safe regardless of what threads the symbols are being materialized on. These methods are an essential part of the MaterializationResponsibility API.
I added methods for both, but I haven't added a test yet. I marked in the unit tests which API methods for MR are not yet covered.
I think we also talked about the type hierarchy for the Layers and that in theory we should allow for some polymorphism instead of requiring specific types.
llvm/examples/OrcV2Examples/OrcV2CBindingsVeryLazy/OrcV2CBindingsVeryLazy.c | ||
---|---|---|
149 | @lhames am I right that I need to pass the LLJIT as the Context? | |
llvm/include/llvm/ExecutionEngine/Orc/LLJIT.h | ||
188 ↗ | (On Diff #357817) | @lhames are you okay with making this public? We need either that or a way to get the TM from LLJIT. |
llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp | ||
380 | ||
400 | @lhames the flags returned here are wrong so I might have a mistake somewhere in the conversion code. | |
409 | @lhames Ideas for tests are appreciated :) |
I found a few more issues while getting the example working on Darwin:
- Rather than exposing applyDataLayout, it would be better to add an LLVMOrcLLJITGetDataLayoutString function, then use the existing LLVMSetDataLayout function.
- You should uses strlen rather than sizeof when building the MemoryBuffers for the source -- MemoryBuffer's null termination guarantee is that the character one past the end of the buffer is null, so 'sizeof' causes assertions if the memory following the null terminator happens to be non-zero.
- You should compares mangled+interned symbol names, rather than doing string comparisons on symbol names. That makes the example work regardless of the platform mangling scheme. (Side note: in a real world example we'd want to intern the comparison symbols once and re-use them, but the extra memory management felt like it would just complicate things here so I've opted to re-intern the strings on each call.)
- You'll need to add a LLVMOrcDisposeMaterializationResponsibility function: MaterializationResponsibility objects are uniquely owned -- If you hand it off to a base layer you're off the hook, but if you fail the materialization you need a way to dispose of it. This should be called on MR after you fail the materialization in the example.
I've also added some inline notes about ownership in the comments.
llvm/include/llvm-c/Orc.h | ||
---|---|---|
636–643 | typo in 'lenght'. The comment should include a note that the pool entries are not retained by this operation: The entries need not be released, but must be retained if the caller wants them to outlive any mutating operations on the MR object. | |
647–649 | This should include a note that it does not release the entries. | |
662–665 | Should include a note that the returned entry is not retained -- the caller should retain it if they want it to outlive any mutating operations on the MR. | |
672 | This should include a not that it only frees the list, it does not release the entries. | |
763–765 | This should include a note that the function returns ownership of the instance, and the client is responsible for freeing it (after resolving/emitting, or failing). |
llvm/unittests/ExecutionEngine/Orc/OrcCAPITest.cpp | ||
---|---|---|
432 | -debug-only=orc tells the story: In main defining materializing symbols { ("_other", [Callable]) } In main replacing symbols with MU@0x10d721db0 ("<Absolute Symbols>", { ("_other", [Callable]) }) Adding dependencies for _foo: { (main, { _other }) } In main adding dependencies for _foo: { (main, { _other }) } Adding dependencies for all symbols in { ("_foo", [Callable]) }: { (main, { _other }) } In main adding dependencies for _foo: { (main, { _other }) } In main resolving { ("_foo": 0x00000001000eb8a0 [Callable]) } In main emitting { ("_foo", [Callable]) } Done dispatching MaterializationUnits. You've hit a not-yet-considered corner case: Adding a dependency on a symbol (other) for which there is no corresponding lookup. Materialization is currently only triggered by lookup, not by adding dependencies, so the lookup for foo is left stuck (due to the dependence on other) with no way to trigger materialization of other. A real test of the dependence tracking in the success case would require async lookups. You could:
I don't think you need to implement the APIs required to test this though. For this test I would just go ahead and resolve/emit other too, then this should work. Could you include a FIXME ('write async lookup API, then... ', and the steps listed above) in the test case so that we can go back and do it The Right Way in the future? |
@lhames am I right that I need to pass the LLJIT as the Context?