# Is there an equivalent to cross-language link time optimization via LLVM?

**URL:** <https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625>\
**Category:** General Usage\
**Created:** [February 13, 2026, 7:15pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625 "2026-02-13T19:15:48Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 13, 2026, 7:15pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/1 "2026-02-13T19:15:49Z")

</div>

I’m reading about [this between Rust and C/Fortran/etc](https://github.com/rust-lang/rust/blob/main/src/doc/rustc/src/linker-plugin-lto.md), and here’s [a simple example](https://developers.redhat.com/blog/2020/03/18/cross-language-link-time-optimization-using-red-hat-developer-tools) of constant folding from inlining an integer range summation from Rust into a C program with literal integer inputs. Obviously we don’t routinely deal with linking, but I’m wondering if there’s something between a `ccall` and `llvmcall` where a C library function is called and its LLVM IR is optimized together with the Julia code.

Discussion about other contrast and parallels (thin vs fat LTO versus the whole-program optimization in a Julia process) and when cross-language optimization is beneficial (constant folding is rare, inlining often doesn’t help) is welcome, lots to learn.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 13, 2026, 7:31pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/2 "2026-02-13T19:31:57Z")

</div>

> [@Benny](#):
>
> I’m wondering if there’s something between a `ccall` and `llvmcall` where a C library function is called and its LLVM IR is optimized together with the Julia code.

Sounds like a nice idea for a package.

---

<div class="post-metadata">

**Author:** ![gbaraldi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gbaraldi/32/22101_2.png) [@gbaraldi](https://discourse.julialang.org/u/gbaraldi)\
**Post date:** [February 13, 2026, 7:32pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/3 "2026-02-13T19:32:51Z")

</div>

IIRC enzyme has some version of this. But a package can probably do this without the whole enzyme autodiff.

---

<div class="post-metadata">

**Author:** ![GeorgeGkountouras](https://avatars.discourse-cdn.com/v4/letter/g/77aa72/32.png) [@GeorgeGkountouras](https://discourse.julialang.org/u/GeorgeGkountouras)\
**Post date:** [February 13, 2026, 8:48pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/4 "2026-02-13T20:48:04Z")

</div>

Say I have compiled a dynamic library with juliac. Can I treat it as a static library for whole-program optimization when calling it from C++?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 13, 2026, 9:09pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/5 "2026-02-13T21:09:26Z")

</div>

Dynamic libraries are not static libraries. Nothing to do with juliac.

Furthermore, for LTO the compiler needs more data (LLVM IR, I guess) than just the object code.

---

<div class="post-metadata">

**Author:** ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)\
**Post date:** [February 13, 2026, 10:16pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/6 "2026-02-13T22:16:06Z")

</div>

> [@gbaraldi](#):
>
> But a package can probably do this without the whole enzyme autodiff.

Is it that simple? Doesn’t this require a lot of internals support, to properly cooperate with the existing system?

I think that this would be a _huge_ feature. Compile your shared library with `clang -fembed-bitcode=all` and have a `ccall` variant that permits cross-language inlining. Or maybe a flag for `Libdl.dlopen`, to look for bitcode sections in the library.

For starters, we could compile most of the julia runtime with that, and have most cheap runtime calls inlined.

For seconds, this would make a lot of intrinsics programming much nicer – basically because `immintrin.h` is well documented, while llvm processor intrinsics are an underdocumented mess. So this would enable us to just write the kernel in C (ever tried to use the juicy `aesenc` instructions from julia?)

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [February 13, 2026, 10:16pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/7 "2026-02-13T22:16:37Z")

</div>

This was an example of attempting to autodiff in Julia with Enzyme code generated by Numba in Python: [Wrong gradient of a `cfunc` decorated Numba function · Issue #2505 · EnzymeAD/Enzyme.jl · GitHub](https://github.com/EnzymeAD/Enzyme.jl/issues/2505). But in general, once you have llvm byte code, the front-end language it was originally written in doesn’t matter anymore and you can do all the optimisations you want.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 13, 2026, 10:52pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/8 "2026-02-13T22:52:34Z")

</div>

Never did autodifferentiation let alone Enzyme, so noob question: is that issue implying that Enzyme works or is intended to work on LLVM IR across `ccall` boundaries? What went wrong there exactly, couldn’t tell how the working and failed functions differed after that Numba name-unmangling.

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [February 13, 2026, 11:01pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/9 "2026-02-13T23:01:21Z")

</div>

> [@Benny](#):
>
> Never did autodifferentiation let alone Enzyme, so noob question: is that issue implying that Enzyme works or is intended to work on LLVM IR across `ccall` boundaries?

Yes, Enzyme works at the LLVM level, which means it couldn’t care less about the frontend language: as long as it receives LLVM IR/bitcode, it can do anything. And Enzyme is “just” an optimisation pass, so the same applies to other passes.

> [@Benny](#):
>
> What went wrong there exactly, couldn’t tell how the working and failed functions differed after that Numba name-unmangling.

If you want a more positive example:

> [@Get Numba LLVM IR differentiated in Julia using Enzyme.jl](https://discourse.julialang.org/t/get-numba-llvm-ir-differentiated-in-julia-using-enzyme-jl/131255/11):
>
> You made it so easy for me to fix it! Indeed I was making a call to the Numba’s internal function, namely \_ZN8\_\_main\_\_4funcB2v1B52c8tJTIeFIjxB2IKSgI4CrvQClUYkACQB1EiFSRRB9GgCAA\_3d\_3dEdddd which returns an i32, 0 for the success and 1 for Python exception. The first argument double\* %retptr is where the result is stored, the second argument { i8\*, i32, i8\*, i8\*, i32 }\*\* is a pointer for Numba’s exception info block (%excinfo), and then the rest four arguments for a, w, p and t as double’s. Wher…

---

<div class="post-metadata">

**Author:** ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)\
**Post date:** [February 13, 2026, 11:07pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/10 "2026-02-13T23:07:11Z")

</div>

> [@foobar\_lv2](#):
>
> Compile your shared library with `clang -fembed-bitcode=all` and have a `ccall` variant that permits cross-language inlining.

It has been suggested a few times in the past that BinaryBuilder enables `-fembed-bitcode=all` by default, but that a significant infrastructure change that we never did it in practice, but in theory it should be doable.

---

<div class="post-metadata">

**Author:** ![rayegun](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rayegun/32/26729_2.png) [@rayegun](https://discourse.julialang.org/u/rayegun)\
**Post date:** [February 14, 2026, 12:33am UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/11 "2026-02-14T00:33:05Z")

</div>

It’s possible to do this manually in both directions, I have done a fair amount of experiments but it involved a lot of manual interaction with Clang and LLD. The biggest problem I ran into was caching and cache invalidation (of Julia code in the Julia → C direction which I investigated the most). That might be better now with things like CompilerCaching.jl Various other problems included mapping between architecture tuples (there’s some weird permutations of macOS/darwin when doing lto) and managing the bitcode in memory/on disk.

ClangCompiler.jl is/was an attempt at getting some of this working less manually but I’m not sure the status of that project now.

BB2 with the local compiler shards would also be a massive help here, managing the toolchain was a huge headache.

---

<div class="post-metadata">

**Author:** ![GeorgeGkountouras](https://avatars.discourse-cdn.com/v4/letter/g/77aa72/32.png) [@GeorgeGkountouras](https://discourse.julialang.org/u/GeorgeGkountouras)\
**Post date:** [February 14, 2026, 3:38pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/12 "2026-02-14T15:38:54Z")

</div>

Let me rephrase. All C++ and julia code is known. C++ calls `my_long_julia_computation()`, and this can be resolved statically.

Is there a way to do whole-program optimization then?

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [February 14, 2026, 5:37pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/13 "2026-02-14T17:37:04Z")

</div>

Per the discussion above, seems like reasonable functionality, but no one bothered to implement it yet.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [February 14, 2026, 5:46pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/14 "2026-02-14T17:46:36Z")

</div>

While JuliaC’s README does mention producing intermediate code, cross-language LLVM compilation has not been implied to be a goal anywhere. If it eventually does happen, you’ll likely be using Clang to compile and link the C++ program that uses the JuliaC library. Fortunately there’s no reason for you to wait. `my_long_julia_computation` is unlikely to benefit from significant optimizations because long methods are extremely unlikely to be inlined or to benefit from inlining, and shaving a couple nanoseconds of a function call from a long-running computation is negligible. This topic is more about different languages trading small functions for which they don’t already have simple equivalents.

---

<div class="post-metadata">

**Author:** ![rayegun](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rayegun/32/26729_2.png) [@rayegun](https://discourse.julialang.org/u/rayegun)\
**Post date:** [March 3, 2026, 11:19pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/15 "2026-03-03T23:19:32Z")

</div>

To clarify my statement. This is completely possible, you’ll just have to do the legwork.

Compile the C++ code with the right LTO flags using Clang (`fembed-bitcode` I believe is one you will need, although these flags are a little complex) into object files, and then use GPUCompiler.jl or JuliaC.jl to generate the bitcode files. Then use `lld` to do LTO.

If you’re looking for this capability to be built into Julia then you will probably be waiting a while though, it is not a trivial thing to manage that toolchain. I would expect in the long run that someone will build the tools to do this all in-memory but it’s a very arcane use-case with a pretty high maintenance overhead.

---

<div class="post-metadata">

**Author:** ![obsidianjulua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/obsidianjulua/32/221495_2.png) [@obsidianjulua](https://discourse.julialang.org/u/obsidianjulua)\
**Post date:** [March 5, 2026, 12:30am UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/16 "2026-03-05T00:30:42Z")

</div>

RepliBuild ingests C/C++ source code, compiles it through an LLVM/MLIR pipeline, introspects DWARF debug metadata, and emits type-safe Julia bindings with correct struct layout, enum definitions, and calling conventions. Functions that require non-trivial ABI handling (packed structs, unions, virtual dispatch) are automatically routed through a JIT tier built on a custom MLIR dialect. I copied this from a README in the RepliBuild.jl pkg if its relavent.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [March 5, 2026, 7:54am UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/17 "2026-03-05T07:54:57Z")

</div>

The [zero-cost LTO option](https://github.com/obsidianjulua/RepliBuild.jl/blob/main/docs/src/guide.md#zero-cost-lto-dispatch) seems to fit the bill, but is the `llvmcall` input `(mod_ir::String, entry_fn::String)` backwards there?

```julia-auto
const LTO_IR = isfile(LTO_IR_PATH) ? read(LTO_IR_PATH, String) : ""

function vector_dot(a::Ptr{Cvoid}, b::Ptr{Cvoid}, n::Cint)::Cdouble
   if !isempty(LTO_IR)
       return Base.llvmcall(("_Z10vector_dotPdPdi", LTO_IR),
                            Cdouble, Tuple{Ptr{Cvoid}, Ptr{Cvoid}, Cint},
                            a, b, n)
   else
...

```

That aside, automatically generating inlineable (I’m assuming) Julia functions wrapping `llvmcall`s sounds convenient, lets us write “pure” Julia after. C++ has templates, so I’m wondering now how those and other languages’ generic functions could be supported with some concrete types specified from Julia.

---

<div class="post-metadata">

**Author:** ![vchuravy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/vchuravy/32/8_2.png) [@vchuravy](https://discourse.julialang.org/u/vchuravy)\
**Post date:** [March 5, 2026, 8:37am UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/18 "2026-03-05T08:37:54Z")

</div>

I did a prototype ages ago, which is what the Enzyme capabilities are based off [LibBC.jl/src/LibBC.jl at master · vchuravy/LibBC.jl · GitHub](https://github.com/vchuravy/LibBC.jl/blob/master/src/LibBC.jl)

The general gist is that yes once you compile with Clang and `-fembed-bitcode` the shared library will contain a bit code section that you can read and parse.

The big challenge here is that you need to match Clang and Julia (LLVM bit code is version dependent) and you need to correctly implement the expected semantics of C/C++ (global variables being uniques).

It is something that currently works for simple libraries, but for anything large and complex it would take significant amount of work and then additional work on the infrastructure (to get shared libraries with the right bitcode). Personally I see better C++ interoperability through something like ClangCompiler.jl as a more impactful improvement.

---

<div class="post-metadata">

**Author:** ![obsidianjulua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/obsidianjulua/32/221495_2.png) [@obsidianjulua](https://discourse.julialang.org/u/obsidianjulua)\
**Post date:** [March 5, 2026, 2:19pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/19 "2026-03-05T14:19:57Z")

</div>

### New: Cross-Language LTO (Link-Time Optimization)

- **Zero-Cost Abstractions via `Base.llvmcall`** — When `enable_lto = true`, the compiler now emits an LLVM Bitcode payload (`_lto.bc` and `_lto.ll`). The generated Julia wrapper intercepts safe primitive/pointer FFI boundaries and dynamically loads the LLVM IR at parse-time, routing the execution through `Base.llvmcall` instead of `ccall` to allow Julia’s JIT to inline C++ code directly into Julia hot loops.

### New: MLIR Ahead-Of-Time (AOT) Thunks

- **Static C++ Vtable Dispatch** — Introduced `aot_thunks` flag in the configuration to statically compile MLIR JLCS thunks directly into `.o` artifacts, linking them into a native `_thunks.so` companion library during the `build()` phase.
- Generated `Wrapper.jl` now conditionally emits purely static `ccall` bindings that bypass the `JITManager` runtime entirely for zero-overhead, statically-verifiable polymorphic execution.

---

<div class="post-metadata">

**Author:** ![obsidianjulua](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/obsidianjulua/32/221495_2.png) [@obsidianjulua](https://discourse.julialang.org/u/obsidianjulua)\
**Post date:** [March 5, 2026, 6:59pm UTC](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625/20 "2026-03-05T18:59:49Z")

</div>

**Declarative Template Resolution** — Added `templates` and `template_headers` to the `[types]` config. The compiler automatically generates dummy C++ source files to force Clang to instantiate the requested types (e.g. `std::vector<int>`), guaranteeing they appear in the DWARF debug metadata for MLIR processing and FFI wrapping.

[Next page](https://discourse.julialang.org/t/is-there-an-equivalent-to-cross-language-link-time-optimization-via-llvm/135625.md?page=2)
