About using "omp" dialect in MLIR

Hi,

Is anyone can give me help on using “omp” dialect in MLIR? In MLIR website, there is “omp” dialect, the operations in “omp” dialect are also listed. Then I want to use it. But when I use “mlir-opt --help”, there is no option to invoke a pass to convert “omp” dialect code into llvm or other dialect code.

Can any one tell me is “omp” dialect ready to use or not? If it is ready to use, how to use it?

Thank you so much for your help. I really appreciate your help.

Best,
Ruiqin

In general looking at the test is a good way to get started. For example the OpenMP to LLVM basic testing is here: https://github.com/llvm/llvm-project/blob/master/mlir/test/Target/openmp-llvm.mlir

Hi Mehdi,

Thank you for your reply. But I am still not clear for that… I am sorry.
If I have the following mlir code containing omp.parallel, how do I compile (i.e. using which option to lower the “omp” dialect operation into llvm code) and run it?

module {
  func @main() {
    %1 = alloc() : memref<100000xi32>    
    %i0 = constant 0 : index
    %i100 = constant 100 : index
    %i1 = constant 1 : index
    %i2 = constant 2 : index

    omp.parallel{
      scf.for %arg0 = %i0 to %i100 step %i1{
        %arg0_cast = index_cast %arg0 : index to i32 
        call @print_i32(%arg0_cast) : (i32) -> ()
      } 
    }
    return
  }
  func @print_i32(i32)
}

Thank you for your help!

I don’t believe that what you’re doing here will yield a parallel loop, I don’t think the OpenMP dialect has parallel loop at this point. You’re using the omp.parallel for which the current lowering was implemented here: https://reviews.llvm.org/rGd9067dca7ba7cda97a86ec22106e06ffc700ecbf

@kiranchandramohan who implemented it may help you.

Thanks @mehdi_amini for the ping and @rqtian for the interest.

OpenMP dialect is a work in progress. We have not yet implemented the openmp loop operation. So you will probably not get what you want (split the loop across multiple threads). We also have not set up pass pipelines to convert from omp dialect (with any region) to llvm. So you will have to manually do that.

I was hoping that you can use mlir-opt with -convert-scf-to-std and -convert-std-to-llvm to convert to llvm-dialect and then use mlir-translate with -mlir-to-llvmir to generate llvm IR. But the std-to-llvm step is hitting an assertion failure. I have not checked what is the reason.

I see the following trivial example works for the std to llvm IR flow.

module {
 func @main() {
   %i1 = constant 1 : index

   omp.parallel{
     call @print_index(%i1) : (index) -> ()
     omp.terminator
   }
   return
 }
 func @print_index(index)
}
./bin/mlir-opt -convert-std-to-llvm  t1.mlir &> t2.mlir
./bin/mlir-translate -mlir-to-llvmir t2.mlir  

Hi kiranchandramohan,

Thank you so much for your reply. So for your example code, after using the two commands, how to run it? I should use mlir-cpu-runner, right? What options should I give to mlir-cpu-runner? I used the following one but got errors:

/build$ cat t2,mlir | ./bin/mlir-cpu-runner -O3 -e main -entry-point-result=void -shared-libs=./lib/libmlir_runner_utils.so,./lib/libmlir_c_runner_utils.so
JIT session error: Symbols not found: [ __kmpc_fork_call, __kmpc_global_thread_num, print_index ]
Error: Failed to materialize symbols: { (main, { _mlir_main, main, _mlir_main..omp_par }) }

Any suggestions will be helpful! Thank you so much!

I have not tried mlir-cpu-runner. The kmpc symbols are from the libomp library of llvm. You can enable openmp when you build llvm,mlir etc.
-DLLVM_ENABLE_PROJECTS=clang;llvm;mlir;openmp

I guess you have to define print_index somewhere also.

You need to make sure the libomp is built a as a dynamic library and append the path to the library to the -shared-libs flag.

There is no index type at the LLVM dialect level, which is what the runner consumes. One needs to cast the index to a fixed-width integer type and use an appropriate printing function defined here llvm-project/CRunnerUtils.cpp at 139018265bfecec450261292f24f8bf3c37be96f · llvm/llvm-project · GitHub

1 Like

Thanks @ftynse for the reply.

While using mlir-opt to convert std to llvm, a type mismatch error was hit for basic block arguments. Error and mlir code given below. Also observed that when the basic blocks are without arguments the conversion happens fine. Looking through the code, it seems the basic block arguments are not being converted in the OpenMP parallel region. Also saw in the dialect conversion document that block arguments are special and a custom hook (convertRegionTypes) should be called for dialect conversion. Does that mean that we have to write a dialect conversion? Or can mlir-opt be invoked with some options to convert the basic block arguments in the OpenMP region.
(Note: we keep the OpenMP operations along with llvm dialect and translate them along with llvm dialect ops).

./bin/mlir-opt -convert-std-to-llvm t30.mlir
t30.mlir:11:7: error: type mismatch for bb argument #0 of successor #0
      br ^bb1(%c1: index)
      ^
t30.mlir:11:7: note: see current operation: "llvm.br"(%19)[^bb1] : (!llvm.i64) -> ()
 module {
  func @main() {
    %0 = alloc() : memref<100000xi32>
    %c0 = constant 0 : index
    %c100 = constant 100 : index
    %c1 = constant 1 : index
    %c2 = constant 2 : index
    omp.parallel {
      br ^bb1(%c1: index)
    ^bb1(%a: index) :
      %1 = index_cast %a : index to i32
      call @print_i32(%1) : (i32) -> ()
      br ^bb3
    ^bb3:
      omp.terminator
    }
    return
  }
  func @print_i32(i32)
}

The type conversion for the block arguments (be they in the first block or not) is controlled by the pattern that converts the parent op. Standard-to-LLVM dialect conversion cannot know how you want to convert the types in non-Standard operations. So if omp.parallel supports both index and !llvm.i* types, you need to have a pattern that converts omp.parallel to itself while updating the types.

1 Like