I knew that Julia uses LLVM. I also knew that Julia has something called IR, but i didn't know what it was. So Julia's IR is probably LLVM's IR...
For some examples, the compiler plugins interface acts on typed IR, so things like Diffractor for automatic differentiation or JET static analysis and EscapeAnalysis.jl act there. Notably, Zygote automatic differentiation acted on the untyped IR. GPUCompiler.jl does some operations on the typed IR then lowers to the LLVM IR level and then intercepts the normal compilation process to choose alternative backends (e.g. compile to .ptx for compilation of native Julia to CUDA). Enzyme.jl uses the Enzyme LLVM-based automatic differentiation, so it does some actions on the typed IR before lowering to LLVM and then injecting the Enzyme LLVM pass before compiling via GPUCompiler.jl (now to the normal CPU backend).
The front-end compiles your input language (C, C++, Rust, etc) down to a low-level language called an Intermediate Representation. Then the back-end of the compiler optimizes the IR and compiles it into object code. A family of compilers will usually share the back-end.
This kind of split allows for deduplication across different compilers in the same family, and also makes it easier to design a new language without having to fully re-implement everything about the compiler yourself.
In GCC, these are bundled together as a common project afaik. In Clang/LLVM, these are split into the front-end (clang) and the back-end (LLVM).