[RFC] omp.module and omp.function vs dialect attributes to encode openmp properties

For types, there is a precedent of attaching an Interface at build time. Also check the following documentation. Interfaces - MLIR

Where will you be adding these attributes? During lowering from parse-tree to MLIR? Are these attributes well defined in LLVM (like function attributes LLVM Language Reference Manual — LLVM 18.0.0git documentation)? Could you give some more information? The only concern is if you would want to use the OpenMPIRBuilder to add these attributes, then it might be difficult to do that since a normal LLVM function will be translated to LLVM IR (in mlir/lib/Target/LLVMIR/Dialect/LLVMIR/LLVMToLLVMIRTranslation.cpp) and wouldn’t have access to the OpenMPIRBuilder. If it is an omp.function there will be a handle to OpenMPIRBuilder during translation (in mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp).

These are good points. My thinking was that the attributes would either well defined in LLVM, or they are attributes to encode extra information needed for the OpenMP to LLVMIR translation, which can be safely ignored by the generic function lowering. If that turns out to be false, it would be fairly easy to create the omp.funcion/module ops if needed.

Probably I missed something. What will be in the mlir module when Flang is invoked in OpenMP target target mode?

  • declare target stuff
  • canonical loops with a target directive

To get some sense of where people are with this I created a poll: What solutions do you find acceptable/compelling to represent OpenMP specific information that needs to be passed from the flang front end down to the LLVM-IR codegen in mlir?

  • New operations that do not overlap with existing ops e.g. omp.declare_target, but not omp.func (module level has to be handled separately)
  • Global variables to encode information
  • Interfaces (Combined with first two options) to querying simpler by traversing module/functions.
  • Dialect attributes (discardable) on module and func.func ops, similar to wha the LLVMIR dialect does.
  • New omp.module and omp.func ops with inherent attributes

0 voters

It will be great if you can provide some information about the following.
→ What are the changes that you want to see in a function that is meant for offloading at the LLVM lR level compared to the same function that is not meant for offloading?
→ Is there a role for the OpenMP IRBuilder in the lowering of these functions. We would like the code in translation to be as minimal as possible?

I checked that Clang IR (CIR - an LLVM umbrella project for implementing MLIR for clang) does not support function attributes. I opened an issue and I asked about it:

IMO this issue is very similar to our problem of encoding OpenMP properties and CIR developers’ feedback will be valuable.

1 Like

Right now, what I know about is the declare target stuff. For openmp in general maybe, variant information. It might be possible to filter things when going from AST->MLIR to avoid representing any of this. Potentially it could be useful for libraries in MLIR if we want to keep things in a single file/module.

I don’t know what the role of the OpenMPIRBuilder will be right now for whole funcitons.It may be that it will need to do some work for creating metadata. For target regions and outlining etc the OpenMPIRBuilder will do most of the work.

Hi @DominikAdamski

In CIR we have cir.func and will likely add a custom argument attribute on top of DictArrayAttr since we usually like to be explicit about the things CIR support, and prevent the possibility of silently dropping information over passes.

I add the link from other MLIR topic which is connected to this discussion ( Some questions about mlir lower - #5 by ftynse ).

User wanted to add new target architecture. He was told to create a new target dialect which needs to be lowered directly to LLVM IR, because LLVM MLIR dialect does not support target specific features.

An argument for having an omp.module is that it could make adding omp.module specific global data flags concise as well, as we could just handle it on lowering of the operation to LLVM-IR.

For example, for GPU’s several flags in the form of globals are added for the module (based on frontend arguments when they are specified to the compiler, otherwise they are set to some defaults): llvm-project/CGOpenMPRuntimeGPU.cpp at main · llvm/llvm-project · GitHub

So having a omp.module operation that supports this information and carries it down to the OpenMPToLLVMIRTranslation level for it to handle appropriately using the OMPIRBuilder seems like it could be useful.

Makes sense. In that case maybe it would be good to be consistent and have omp.func as well.

Other reasons for having an omp.func (i guess it should be omp.target_func) are:
→ Transformations like inlining or specialization will not apply to the omp.func. But if it stays in the native dialect then these transformations can happen.
→ Interfacing with the OpenMPIRBuilder is achieved directly, since this operation will be available for lowering in OpenMPToLLVMIRTranslation.cpp.
One point to think about is, would the presence of omp.func require omp.call as well?

However, I think the Translation code might not be as generalized as the rest of MLIR and can probably only currently handle OpenMP Operations inside blocks in LLVM Functions. Module Translation might also have such restrictions for modules.

Yes, that is one thing that might cause problems. From what I have gathered attributes added to modules and functions are generally preserved because other dialects rely on this already, so it seems to be used like metadata in LLVM (which was originally supposed to be discardable also).

Naïve question as I am still getting to grips with MLIR and likely will be for a long while, but would it not be possible to create the appropriate hooks for the more “generic” passes like inlining and specialization to work on an omp.func using an interface? I recall something similar occurring in the Toy tutorial, except for some of the toy operations (I think it’s in Chapter 4). I’m not sure it’s applicable to the problem though but I thought I’d mention it incase it is.

Of course you can, see the Toy examples:

You can implement the DialectInlinerInterface for the OpenMP dialect and forbid all inlining operations.

Yes, it is possible. I was assuming that we would not want to inline or specialize an omp.func because it is destined to be offloaded. And hence omp.func is a good choice here.

We are currently aiming to go with the apply dialect attributes to the existing builtin.module approach as opposed to the other choices, unless someone has objections to it. As it appears to be the simplest but most tried and true method of the options with the FIR and the LLVM dialect already currently doing similarly in Flang with fir.kindmap and llvm.data_layout.

We (myself and Jan and some other folk at AMD) have had a look into the omp.module approach, managing to get a working implementation where we can use an omp.module when compiling for OpenMP rather than the regular builtin.module that Flang uses and have it pass the existing test suite. However, the lack of Operation inheritance in MLIR (at least fully supported) makes the changes quite significant, even when utilising an interface class to ease the burden of having two interchangeable module operation types. It’s likely that this change would not be received welcomingly without a very good reason for it, as it would affect the Flang community as well as the OpenMP community (even if there is a lot of cross-over between them).

So until we have this good reason(s), we are opting for the path of least resistance which has also been trialed and errored by other dialects. One benefit of this attribute approach is that it can always be up-scaled to the module approach when we see a critical use-case for it.

What we saw with the DLTI and and DataLayout interface are that attributes are used to encode data (same thing is done in LLVMIR dialect). The DataLayout interface is there so that many dialects can make use of it without referring to DLTI directly when doing queries, and it gives some flexibility how to implement the interface in case some other dialect needs to do something differently. If the uses are within one dialect then just using attributes directly seems to be acceptable.