Parallel compilation with LLJIT

I’m trying to JIT compile multiple modules in parallel. Each module is in its own ThreadSafeModule, but they share a common LLJIT instance. The program crashes during the compilation when symbols from different modules are lookup()ed concurrently. Here’s a condensed version of my code, with some error handling omitted for brevity:

// Parse module IR from name,src and lookup fn_name symbol
void th_fn(LLJIT *jit, const char *name, const char *src, const char *fn_name) {
  auto ctx = make_unique<LLVMContext>();
  SMDiagnostic diag;
  if (auto module = parseIR(MemoryBufferRef(src, name), diag, *ctx)) {
    cout << "Module " << name << " parsed successfully" << endl;
    if (auto err = jit->addIRModule(ThreadSafeModule(move(module), move(ctx)))) {
      cout << "Module " << name << " error on add: " << toString(move(err)) << endl;
      return;
    }
    cout << "Module " << name << " added successfully" << endl;
    auto addr = jit->lookup(fn_name);
    if (!addr) {
      cout << "Module " << name << " error on lookup: " << toString(addr.takeError()) << endl;
      return;
    }
    cout << "Module " << name << " addr: " << addr->getAddress() << endl;
    return;
  }
}

extern "C" LLVMErrorRef LLVMFFITest() {
  auto src_1 =
"    define i32 @test1_fn(i32 %arg1, i32 %arg2) {\n"
"    entry:\n"
"      %sum = add i32 %arg1, %arg2\n"
"      ret i32 %sum\n"
"    }";
    auto src_2 =
"    define i32 @test2_fn(i32 %arg1, i32 %arg2) {\n"
"    entry:\n"
"      %sum = sub i32 %arg1, %arg2\n"
"      ret i32 %sum\n"
"    }";

  initializeCore(*PassRegistry::getPassRegistry());
  LLVMInitializeNativeTarget();
  LLVMInitializeNativeAsmPrinter();

  auto jit = LLJITBuilder().create();
  if (!jit) {
    return wrap(jit.takeError());
  }

  std::thread th1(th_fn, jit->get(), "mod1", src_1, "test1_fn");
  std::thread th2(th_fn, jit->get(), "mod2", src_2, "test2_fn");
  th1.join();
  th2.join();

  return LLVMErrorSuccess;
}

A typical output is:

Module mod2 parsed successfully
Module mod1 parsed successfully
Module mod2 added successfully
Module mod1 added successfully
Segmentation fault (core dumped)

but sometimes assertions fail instead, like:

sim-llvm: llvm-project/llvm/include/llvm/Support/Allocator.h:191: void *llvm::BumpPtrAllocatorImpl<llvm::MallocAllocator, 4096, 4096, 128>::Allocate(size_t, llvm::Align) [AllocatorT = llvm::MallocAllocator, SlabSize = 4096, SizeThreshold = 4096, GrowthDelay = 128]: Assertion `AlignedAddr + SizeToAllocate <= (uintptr_t)End && "Unable to allocate memory!"' failed.

or

sim-llvm: llvm-project/llvm/include/llvm/ADT/DenseMap.h:1215: llvm::DenseMapIterator<llvm::StringRef, llvm::detail::DenseSetEmpty, llvm::DenseMapInfo<llvm::StringRef>, llvm::detail::DenseSetPair<llvm::StringRef>, false>::DenseMapIterator(llvm::DenseMapIterator::pointer, llvm::DenseMapIterator::pointer, const llvm::DebugEpochBase &, bool) [KeyT = llvm::StringRef, ValueT = llvm::detail::DenseSetEmpty, KeyInfoT = llvm::DenseMapInfo<llvm::StringRef>, Bucket = llvm::detail::DenseSetPair<llvm::StringRef>, IsConst = false]: Assertion `isHandleInSync() && "invalid construction!"' failed.

It works correctly when the lookup is serialized, so it seems that LLJIT isn’t thread-safe. Is there an easy way to compile multiple modules into one JITDylib in parallel?

Edit: This is on the main branch from 2022-02-26 (274ec425dcc3e3f637dd006c5e9ae33bd0e2e917) with LLVM compiled as Debug.