[RFC] Add RISC-V Vector Extension (RVV) Dialect

I took the initiative and I’ve added the required intrinsics to LLVM dialect (D127100). The next couple steps will be extending vector.insert or vector.shape_cast (or both in tandem) to allow for a higher level operation that casts a fixed-length vector into a scalable vector. I already have a patch downstream that uses this functionality to perform VLS.

At this point, and seeing that there are already a bunch of us interested in VLS, maybe we should decide how we want to approach this conversion from the vector dialect. I’ll open a new thread to discuss this topic specifically, where I will elaborate my thoughts on this.

Hello everyone, I am new to this development, I don’t have a lot of experience, I have a question for you.Scalar codes are converted into RVV intrinsic functions via MLIR and they are mapped to hardware-specific operations. Whether this line of practice is feasible?

This proposal aimed to map RVV-specific vector operations to RVV or VP intrinsics. If you wish to vectorize scalar code, you would also need a vectorizer. Furthermore, we have more discussions on the vector-related topic, which can be found at the following links:

@zhanghb97 I am new to this field(MLIR, RVV). I’ve got some questions.Please enlighten me.

Is RVV Dialect still under development? I noticed you said you were using VP intrinsics for research and projects.

And do VP intrinsics have any trouble in expressing RVV?

I read the document of Buddy Compiler. Developer could use Polygeist to convert C/C++ code into MLIR. But it currently doesn’t supporting conversion of RVV intrinsic calls.

For example, here is my C++ code:

void axpy_vector(double a, double *dx, double *dy, int n) {
  int i;
  long gvl = __riscv_vsetvl_e64m1(n); // intrinsic
  for (i = 0; i < n;) {
    // intrinsic
    gvl = __riscv_vsetvl_e64m1(n - i);
    vfloat64m1_t v_dx = __riscv_vle64_v_f64m1(&dx[i], gvl);
    vfloat64m1_t v_dy = __riscv_vle64_v_f64m1(&dy[i], gvl);
    vfloat64m1_t v_res = __riscv_vfmacc_vf_f64m1(v_dy, a, v_dx, gvl);
    __riscv_vse64_v_f64m1(&dy[i], v_res, gvl);
    i += gvl;
  }
}

If I want to extend Polygeist, how to convert intrinsic(__riscv_vsetvl_e64m1, ..) into MLIR?

What dialect of operation are they supposed to be mapped to in MLIR?

I answered your question in the RFC you link, but just to not leave your question hanging here:

The operation to call functions like C intrinsics lives in the func dialect, and it’s func.call (not very exciting). The operation to call LLVM IR intrinsics lives in the llvm dialect, and it’s llvm.call_intrinsic.

You can implement something like llvm.call_intrinsic by declaring the LLVM intrinsic and using func.call, in that sense llvm.call_intrinsic provides some additional convenience and checks.

The key things you need to know are:

  1. C intrinsics and LLVM IR intrinsics are not the same, the former are implemented using the later, and it doesn’t have to be a 1 to 1 mapping.
  2. C intrinsics are part of Clang, LLVM IR intrinsics built into LLVM, and directly visible by MLIR through its dependency to LLVM.
  3. To call a function, you use func.call from the func dialect; to call an LLVM IR intrinsic, you can use llvm.call_intrinsic from the llvm dialect.

From there, it’s up to you how you want to approach it. It can be a bit of busy work, but it’s pretty simple.

1 Like

@javiersetoain Thanks for the detailed explanation.

Yes, it depends on your purpose @tju_panyizhe

From my perspective, mapping C intrinsics to MLIR dialect operations somewhat resembles raising the abstraction level (going from LLVM IR to MLIR).

The original motivation for working on the RVV dialect and using VP intrinsics was not to interface with C intrinsics. The goal from my side was to compile higher-level MLIR IR (e.g., Linalg) into RVV-specific dialect, and eventually generate better RISC-V code.

We maintain an implementation in buddy-mlir. In my experience, using an RVV dialect setvl operation together with VP intrinsics can cover many scenarios, and the scalable vector type can help implicitly control LMUL.

In my use cases, there are no significant issues. (There were some code generation problems in very early stage, but those have already been resolved.)

@tju_panyizhe Hope this helps. Feel free to reach out if you have further questions.

1 Like