How MLIR addresses the issue of multiple frontends end up reimplementing significant pieces of infrastructure to support the need for analysis and transformation?

Hello

I’m new to both MLIR & LLVM, and I am interested in learning MLIR.

When I was going through the MLIR Toy tutorial, from Chapter 2, I am quoting a paragraph which explains what kind of issue is MLIR addressing in compiler world

Other compilers, like LLVM (see the Kaleidoscope tutorial), offer a fixed set of predefined types and (usually low-level / RISC-like) instructions. It is up to the frontend for a given language to perform any language-specific type-checking, analysis, or transformation before emitting LLVM IR. For example, Clang will use its AST to perform not only static analysis but also transformations, such as C++ template instantiation through AST cloning and rewrite. Finally, languages with construction at a higher-level than C/C++ may require non-trivial lowering from their AST to generate LLVM IR.

As a consequence, multiple frontends end up reimplementing significant pieces of infrastructure to support the need for these analyses and transformation. MLIR addresses this issue by being designed for extensibility. As such, there are few pre-defined instructions (operations in MLIR terminology) or types.

Which makes me wonder, how multiple frontends end up reimplementing significant pieces of infrastructure to support the need for these analyses and transformation.

My questions:

  1. What it actually meant by multiple frontend? I have a blurry idea about this issue. What I understood, this issue means, implementing similar kind of analysis and transformation again and again. For example I have a C code. Now if I employ both clang and llvm on same src code, is that actually multiple frontend?

  2. How actually MLIR is addressing the issue of multiple frontend?

I would be really grateful if some explains me this paragraph in bit more easier way. Better with an simple example.

Thanks in advance!

Frontend here means the “language specific” part of the compiler. That can be clang for C/C++ or flang for Fortran, or swift-frontend for the Switch language.

Swift, Rust, and flang have an IR that is dedicated to these language. They all reimplemented the infra (except flang which uses MLIR). Another example would be XLA (which predates MLIR) and implemented all the infrastructure for its IR…

Sorry it took a while to reply. I was studying your answer.

So if the “frontend” means “language specific” part of the compiler, and they are responsible for generating the IR, then considering your given example

  • clang is responsible for the language-specific type-checking, analysis, or transformation of C/C++
  • flang is responsible … of Fortran
  • switch frontend is responsible … of Switch Language

And infra refers to the tools and systems that help build the compiler. It includes things like parsers, analyzers, and code transformers.

multiple frontends end up reimplementing significant pieces of infrastructure to support the need for these analyses and transformation.

AFAIK, for one language, there is one language frontend. But multiple frontends means that more language frontend are doing similar thing again and again for just one language (e.g. C++). Considering the clang frontend example, does clang frontend and llvm frontend are separate frontends that are implied on C++? If that so, then the answer makes sense.

It is up to the frontend for a given language to perform any language-specific type-checking, analysis, or transformation before emitting LLVM IR. For example, Clang will use its AST to perform not only static analysis but also transformations, such as C++ template instantiation through AST cloning and rewrite.

IMO, What I generally understand about the transformation, is that we have an IR. And then we are implementing different kind of IR transformation to make the existing IR more resource/execution efficient. So the language frontend transformation you talked about, are their scope is limited to before emitting the LLVM IR? Or they have also something to do with after emitting the LLVM IR? For example, Clang will use it’s AST generator to perform static analysis + transformations, such as C++ template instantiation through AST cloning and rewrite. So this transformation happens before the LLVM IR generation?

If the answer is yes, does that mean, we really donot need to do those special transformations (e.g. for C++ template, or for python like high level language)?
Rather we can generate a simple AST. After that, AST to LLVM IR. And then start implement the transformation to the LLVM IR for efficient/performant execution.

Thus, here comes the MLIR. It only focuses on doing transformations on the IR. So than language frontend specific transformations are not needed.

Do I get the idea?

Thanks in advance!

Not really: we mean frontends for different languages.
We’re talking about LLVM frontends, so “anything above LLVM” is a frontend for LLVM.

Clang is a LLVM Frontend for C++, it’s not like there is a “clang frontend” and a “llvm frontend” as two different things.

You may look at the first 10 slides of the very first presentation about MLIR: https://storage.googleapis.com/pub-tools-public-publication-data/pdf/1c082b766d8e14b54e36e37c9fc3ebbe8b4a72dd.pdf

You can see “LLVM IR” and all the arrow getting there.

Historically LLVM was designed so that the frontends would be just building an AST (parsing the language) and emitting LLVM IR. However modern compilers have a layer between the AST and LLVM (which is shown on this picture).
This is the context of the paragraph you’re studying.

As you see, these days it is rather “simple AST” (or even “no AST” for things like ML Frameworks) and then MLIR IR for the DSL and then gradually get to LLVM IR.

Here are some recent slides about the workflow of the Triton compiler: Triton on Hopper
It’s a pretty good example of what MLIR is designed to support.

In this picture, Triton -> TritonGPU -> TritonNvidiaGPU are all MLIR dialects specifically built for Triton.
The presentation explains a bunch of optimization that are done at this level. It would be basically impossible to do on LLVM IR.

1 Like

Thanks a lot for the details explanation. :innocent:

Let me clarify what I understood.

  1. clang is just a frontend for LLVM. Similarly flang,…
  2. Those frontends supposed to be involved with just building the AST and generate the LLVM IR.
  3. Unfortunately, to support modern features of different languages (e.g. Template for C++), there is an extra layer between AST and LLVM IR to handle/perform not only static analysis but also transformations. (as you have given the screenshot)
  1. MLIR comes to address this issue. Rather than performing those analyses and transformations by a language frontend, MLIR takeover from here. MLIR does those analyses and transformations, which results more optimized feed before generating the LLVM IR.
  2. Depending on different cases, there might be “no AST”/“simple AST” > MLIR IR > LLVM IR
  3. Different kind of optimizations can be done before the LLVM IR generation, which is literally impossible to do on LLVM IR. (e.g. Triton)
  4. So finally we achieve more performant execution compared to “Only LLVM IR optimization”.

Unfortunately I cannot access the following link. :worried:

Probably I have found the working MLIR tutorial is here