Adding riscv vector intrinsic to llvm

Hello everyone, I was looking at the following code changes that were made to define vector mask register intrinsics -⚙ D93705 [RISCV] Define vector mask-register logical intrinsics. . I apologize for the basic question and if it seems obvious to others. I see that changes have been made to 2 files, IntrinsicsRISCV.td and RISCVInstrInfoVPseudos.td and the rest are tests.

In the first file I see a class has been added, RISCVBinaryAAANoMask and a definition int_riscv_vmand: RISCVBinaryAAANoMask; I am not able to understand where the actual definion of the function int_riscv_vmand is? I see that the class definition has input and output types but not a definition. How do the internals work for this? Again I apologize if there’s something very basic that I’m not understanding.

Please see llvm/include/llvm/IR/Intrinsics.td:366 for more details. :smiley:
In a word, the name of the intrinsic definition should start with “int_”, and then a target prefix riscv. For int_riscv_vmand, it becomes an intrinsic llvm.riscv.vmand as what you see in LLVM IR.

Thanks for your reply, where is the pattern matching being done so that llvm.riscv.vmand is converted into instructions? And how do I ensure that if I make changes to the pattern matching that it worked correctly?

Patterns are in llvm/lib/Target/RISCV/RISCVInstrInfoVPseudos.td and they are generated by TableGen.
For int_riscv_vmand, its patterns are defined as:

defm : VPatBinaryM_MM<"int_riscv_vmand", "PseudoVMAND">;

And if you follow the inheritance tree, you will find that it finally defines some patterns like:

class VPatBinaryM<string intrinsic_name,
                  string inst,
                  ValueType result_type,
                  ValueType op1_type,
                  ValueType op2_type,
                  int sew,
                  VReg op1_reg_class,
                  DAGOperand op2_kind> :
  Pat<(result_type (!cast<Intrinsic>(intrinsic_name)
                   (op1_type op1_reg_class:$rs1),
                   (op2_type op2_kind:$rs2),
                   VLOpFrag)),
                   (!cast<Instruction>(inst)
                   (op1_type op1_reg_class:$rs1),
                   (op2_type op2_kind:$rs2),
                   GPR:$vl, sew)>;

To be specific, the patterns for llvm.riscv.vmand are something like:

Pat<(... (!cast<Intrinsic>("int_riscv_vmand") ...)),
         (!cast<Instruction>("PseudoVMAND" # ...) ...)>;

And of course, there are patterns for masked instructions and LMUL is taken into consideration for selected instructions.


The infrastructure is good enough for now, so I think if you want to change something, you should make sure your patterns are generated correctly(TableGen will report them if there are some errors). And then, add a test just like all others in llvm/test/CodeGen/RISCV/rvv/ and check if codegen are correct. If it doesn’t meet expectations, you can dump DAGs to see if your patterns are right or the priority of your patterns are higher.

Thank you. I decided to make some changes to vmsne instead. I made some changes by adding a line with vmsnenew instead of vmsne in whichever files it is mentioned in llvm. When I convert a c code example to .s file using my made changes I get the expected opcode vmsnenew.vv in the assembly file. The following is the c example-


#include "common.h"
#include <riscv_vector.h>
#include <string.h>

// reference https://github.com/riscv/riscv-v-spec/blob/master/example/strcmp.s
int strcmp_vec(const char *src1, const char *src2) {
  size_t vlmax = __riscv_vsetvlmax_e8m2();
  long first_set_bit = -1;
  size_t vl, vl1;
  while (first_set_bit < 0) {
    vint8m2_t vec_src1 = __riscv_vle8ff_v_i8m2(src1, &vl, vlmax);
    vint8m2_t vec_src2 = __riscv_vle8ff_v_i8m2(src2, &vl1, vlmax);

    vbool4_t string_terminate = __riscv_vmseq_vx_i8m2_b4(vec_src1, 0, vl);
    vbool4_t no_equal = __riscv_vmsnenew_vv_i8m2_b4(vec_src1, vec_src2, vl);
    vbool4_t vec_terminate = __riscv_vmor_mm_b4(string_terminate, no_equal, vl);

    first_set_bit = __riscv_vfirst_m_b4(vec_terminate, vl);
    src1 += vl;
    src2 += vl;
  }
  src1 -= vl - first_set_bit;
  src2 -= vl - first_set_bit;
  return *src1 - *src2;
}

int main() {
  const int N = 1023;
  const uint32_t seed = 0xdeadbeef;
  srand(seed);

  // data gen
  char s0[N], s1[N];
  gen_string(s0, N);
  gen_string(s1, N);

  // compute
  int golden, actual;
  golden = strcmp(s0, s1);
  actual = strcmp_vec(s0, s1);

  // compare
  puts(golden == actual ? "pass" : "fail");
}

However, when I consider the test at

I get the following error with the failing test where I have changed all mentions of vmsne with vmsnenew in the test file. Why is no match being found even after I made the changes?

Input was:
<<<<<<
             .
             .
             .
            18:  .p2align 2 
            19:  .type intrinsic_vmsnenew_mask_vv_nxv1i8_nxv1i8,@function 
            20:  .variant_cc intrinsic_vmsnenew_mask_vv_nxv1i8_nxv1i8 
            21: intrinsic_vmsnenew_mask_vv_nxv1i8_nxv1i8: # @intrinsic_vmsnenew_mask_vv_nxv1i8_nxv1i8 
            22: # %bb.0: # %entry 
            23:  vsetvli zero, a0, e8, mf8, ta, mu 
next:37'0                                         X error: no match found
            24:  vmsnenew.vv v25, v8, v9 
next:37'0       ~~~~~~~~~~~~~~~~~~~~~~~~~
next:37'1        ?                        possible intended match
            25:  vmv1r.v v26, v0 
next:37'0       ~~~~~~~~~~~~~~~~~
            26:  vmv1r.v v0, v25 
next:37'0       ~~~~~~~~~~~~~~~~~
            27:  vmsnenew.vv v26, v9, v10, v0.t 
next:37'0       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            28:  vmv1r.v v0, v26 
next:37'0       ~~~~~~~~~~~~~~~~~
            29:  ret 
next:37'0       ~~~~~
             .
             .
             .
            46:  .p2align 2 
            47:  .type intrinsic_vmsnenew_mask_vv_nxv2i8_nxv2i8,@function 
            48:  .variant_cc intrinsic_vmsnenew_mask_vv_nxv2i8_nxv2i8 
            49: intrinsic_vmsnenew_mask_vv_nxv2i8_nxv2i8: # @intrinsic_vmsnenew_mask_vv_nxv2i8_nxv2i8 
            50: # %bb.0: # %entry 
            51:  vsetvli zero, a0, e8, mf4, ta, mu 

The context is lacked here so I can’t give more advice.

For tests, you may use llvm/utils/update_llc_test_checks.py to update them.