[LLVM] Add intrinsics for RISC-V Bitmap extension


Zbb extension:

RV32 / 64:

orc.b

https://reviews.llvm.org/D99320

https://reviews.llvm.org/D99319


Zbc extension:

RV32 / 64:

clmul
clmulh
clmulr

https://reviews.llvm.org/D99711

https://reviews.llvm.org/D99712


Zbe extension:

RV32/64:

bcomress
bdecompress

RV64 ONLY:

bcomressw
bdecompressw

https://reviews.llvm.org/D101143

https://reviews.llvm.org/D101144


Zbf extension:

TBD

Zbm extension:

RV64 ONLY:

bmator
bmatxor
bmatflip

https://reviews.llvm.org/D101248

https://reviews.llvm.org/D101249


Zbp extension:

RV32/64:

grev
grevi
gorc
gorci
shfl
shfli
unshfl
unshfli
xperm.n
xperm.b
xperm.h

RV64 ONLY:

grevw
greviw
gorcw
gorciw
shflw
shfli     (For non-existing shfliw)
unshfli   (For non-existing unshfliw)
xperm.w

https://reviews.llvm.org/D100830

https://reviews.llvm.org/D100831


Zbr extension:

RV32 / 64:

crc32b
crc32h
crc32w
crc32cb
crc32ch
crc32cw

RV64 Only:

crc32d
crc32cd

https://reviews.llvm.org/D99009

https://reviews.llvm.org/D99008


Zbt extension:

TBD

Let’s take a look for those patches and what needs to done for those intrinsic on llvm, In:

llvm/include/llvm/IR/IntrinsicsRISCV.td
//===----------------------------------------------------------------------===//
// Bitmanip (Bit Manipulation) Extension

let TargetPrefix = "riscv" in {

  class BitManipGPRIntrinsics
      : Intrinsic<[llvm_any_ty],
                  [LLVMMatchType<0>],
                  [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;
  class BitManipGPRGPRIntrinsics
      : Intrinsic<[llvm_any_ty],
                  [LLVMMatchType<0>, LLVMMatchType<0>],
                  [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;

  // Zbb
  def int_riscv_orc_b : BitManipGPRIntrinsics;

  // Zbc
  def int_riscv_clmul  : BitManipGPRGPRIntrinsics;
  def int_riscv_clmulh : BitManipGPRGPRIntrinsics;
  def int_riscv_clmulr : BitManipGPRGPRIntrinsics;

  // Zbe
  def int_riscv_bcompress   : BitManipGPRGPRIntrinsics;
  def int_riscv_bdecompress : BitManipGPRGPRIntrinsics;

  // Zbm
  def int_riscv_bmator   : BitManipGPRGPRIntrinsics;
  def int_riscv_bmatxor  : BitManipGPRGPRIntrinsics;
  def int_riscv_bmatflip : BitManipGPRIntrinsics;

  // Zbp
  def int_riscv_grev  : BitManipGPRGPRIntrinsics;
  def int_riscv_gorc  : BitManipGPRGPRIntrinsics;
  def int_riscv_shfl  : BitManipGPRGPRIntrinsics;
  def int_riscv_unshfl  : BitManipGPRGPRIntrinsics;
  def int_riscv_xperm_n  : BitManipGPRGPRIntrinsics;
  def int_riscv_xperm_b  : BitManipGPRGPRIntrinsics;
  def int_riscv_xperm_h  : BitManipGPRGPRIntrinsics;
  def int_riscv_xperm_w  : BitManipGPRGPRIntrinsics;

  // Zbr
  def int_riscv_crc32_b : BitManipGPRIntrinsics;
  def int_riscv_crc32_h : BitManipGPRIntrinsics;
  def int_riscv_crc32_w : BitManipGPRIntrinsics;
  def int_riscv_crc32_d : BitManipGPRIntrinsics;
  def int_riscv_crc32c_b : BitManipGPRIntrinsics;
  def int_riscv_crc32c_h : BitManipGPRIntrinsics;
  def int_riscv_crc32c_w : BitManipGPRIntrinsics;
  def int_riscv_crc32c_d : BitManipGPRIntrinsics;
} // TargetPrefix = "riscv"

We defined two new classes that’s derived form base type Intrinsic

class BitManipGPRIntrinsics
      : Intrinsic<[llvm_any_ty],
                  [LLVMMatchType<0>],
                  [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;

will be used for instruction with only one input, like orc.b rd, rs1

class BitManipGPRGPRIntrinsics
      : Intrinsic<[llvm_any_ty],
                  [LLVMMatchType<0>, LLVMMatchType<0>],
                  [IntrNoMem, IntrSpeculatable, IntrWillReturn]>;

will be used for instruction with two input operands, like clmul rd, rs1, rs2

Then we categorize all specific instruction to these two class.


PatGpr was missing, so let’s add it back

llvm/lib/Target/RISCV/RISCVInstrInfo.td
//
// Naming convention: For 'generic' pattern classes, we use the naming
// convention PatTy1Ty2. For pattern classes which offer a more complex
// expansion, prefix the class name, e.g. BccPat.
//===----------------------------------------------------------------------===//

/// Generic pattern classes

+class PatGpr<SDPatternOperator OpNode, RVInst Inst>
+    : Pat<(OpNode GPR:$rs1), (Inst GPR:$rs1)>;
class PatGprGpr<SDPatternOperator OpNode, RVInst Inst>
    : Pat<(OpNode GPR:$rs1, GPR:$rs2), (Inst GPR:$rs1, GPR:$rs2)>;
class PatGprSimm12<SDPatternOperator OpNode, RVInstI Inst>
    : Pat<(OpNode GPR:$rs1, simm12:$imm12), (Inst GPR:$rs1, simm12:$imm12)>;
class PatGprUimmLog2XLen<SDPatternOperator OpNode, RVInstIShift Inst>
    : Pat<(OpNode GPR:$rs1, uimmlog2xlen:$shamt),
          (Inst GPR:$rs1, uimmlog2xlen:$shamt)>;

In order to make clang emit the correct info when Zb* is missing, we need some change in

clang/lib/Sema/SemaChecking.cpp
bool Sema::CheckRISCVBuiltinFunctionCall(const TargetInfo &TI,
                                         unsigned BuiltinID,
                                         CallExpr *TheCall) {
  // CodeGenFunction can also detect this, but this gives a better error
  // message.
+  bool FeatureMissing = false;
+  SmallVector<StringRef> ReqFeatures;
  StringRef Features = Context.BuiltinInfo.getRequiredFeatures(BuiltinID);
-  if (Features.find("experimental-v") != StringRef::npos &&
-      !TI.hasFeature("experimental-v"))
-    return Diag(TheCall->getBeginLoc(), diag::err_riscvv_builtin_requires_v)
-           << TheCall->getSourceRange();
+  Features.split(ReqFeatures, ',');

-  return false;
+  // Check if each required feature is included
+  for (auto &I : ReqFeatures) {
+    if (TI.hasFeature(I))
+      continue;
+    // Make message like "experimental-zbr" to "Zbr"
+    I.consume_front("experimental-");
+    std::string FeatureStr = I.str();
+    FeatureStr[0] = std::toupper(FeatureStr[0]);
+
+    // Error message
+    FeatureMissing = true;
+    Diag(TheCall->getBeginLoc(), diag::err_riscv_builtin_requires_extension)
+        << TheCall->getSourceRange() << StringRef(FeatureStr);
+  }
+
+  return FeatureMissing;
}
clang/include/clang/Basic/DiagnosticSemaKinds.td
-// RISC-V V-extension
+// RISC-V builtin required extension warning
-def err_riscvv_builtin_requires_v : Error<
+def err_riscv_builtin_requires_extension : Error<
-   "builtin requires 'V' extension support to be enabled">;
+   "builtin requires %0 extension support to be enabled">;

So that Clang will print “builtin requires Zb* extension support to be enabled” when “-Xclang +experimental-zbr” is missing when compiling.


Now we need predicates and InstAlias

llvm/lib/Target/RISCV/RISCVInstrInfoB.td
def riscv_gorc   : SDNode<"RISCVISD::GORC",   SDTIntBinOp>;
def riscv_gorcw  : SDNode<"RISCVISD::GORCW",  SDT_RISCVIntBinOpW>;
def riscv_shfl   : SDNode<"RISCVISD::SHFL",   SDTIntBinOp>;
+def riscv_shflw  : SDNode<"RISCVISD::SHFLW",  SDT_RISCVIntBinOpW>;
+def riscv_unshfl : SDNode<"RISCVISD::UNSHFL", SDTIntBinOp>;
+def riscv_unshflw: SDNode<"RISCVISD::UNSHFLW",SDT_RISCVIntBinOpW>;
+def riscv_bcompress    : SDNode<"RISCVISD::BCOMPRESS",   SDTIntBinOp>;
+def riscv_bcompressw   : SDNode<"RISCVISD::BCOMPRESSW",  SDT_RISCVIntBinOpW>;
+def riscv_bdecompress  : SDNode<"RISCVISD::BDECOMPRESS", SDTIntBinOp>;
+def riscv_bdecompressw : SDNode<"RISCVISD::BDECOMPRESSW",SDT_RISCVIntBinOpW>;
...

let Predicates = [HasStdExtZbbOrZbp, IsRV64] in {
def : InstAlias<"rorw $rd, $rs1, $shamt",
                (RORIW  GPR:$rd, GPR:$rs1, uimm5:$shamt), 0>;
} // Predicates = [HasStdExtZbbOrZbp, IsRV64]

+let Predicates = [HasStdExtZbp] in {
+def : InstAlias<"grev $rd, $rs1, $shamt",
+                (GREVI  GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
+def : InstAlias<"gorc $rd, $rs1, $shamt",
+                (GORCI  GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
+def : InstAlias<"shfl $rd, $rs1, $shamt",
+                (SHFLI  GPR:$rd, GPR:$rs1, shfl_uimm:$shamt), 0>;
+def : InstAlias<"unshfl $rd, $rs1, $shamt",
+                (UNSHFLI  GPR:$rd, GPR:$rs1, shfl_uimm:$shamt), 0>;
+} // Predicates = [HasStdExtZbp]
+
+let Predicates = [HasStdExtZbp, IsRV64] in {
+def : InstAlias<"grevw $rd, $rs1, $shamt",
+                (GREVIW  GPR:$rd, GPR:$rs1, uimm5:$shamt), 0>;
+def : InstAlias<"gorcw $rd, $rs1, $shamt",
+                (GORCIW  GPR:$rd, GPR:$rs1, uimm5:$shamt), 0>;
+} // Predicates = [HasStdExtZbp, IsRV64]
+
let Predicates = [HasStdExtZbs] in {

...

let Predicates = [HasStdExtZbp] in {
+def : PatGprGpr<riscv_grev, GREV>;
+def : PatGprGpr<riscv_gorc, GORC>;
+def : PatGprGpr<riscv_shfl, SHFL>;
+def : PatGprGpr<riscv_unshfl, UNSHFL>;
+def : PatGprGpr<int_riscv_xperm_n, XPERMN>;
+def : PatGprGpr<int_riscv_xperm_b, XPERMB>;
+def : PatGprGpr<int_riscv_xperm_h, XPERMH>;
+def : PatGprGpr<int_riscv_xperm_w, XPERMW>;
def : PatGprImm<riscv_shfl, SHFLI, shfl_uimm>;
+def : PatGprImm<riscv_unshfl, UNSHFLI, shfl_uimm>;
def : PatGprImm<riscv_grev, GREVI, uimmlog2xlen>;

...

let Predicates = [HasStdExtZbp, IsRV64] in {
def : Pat<(riscv_rorw (riscv_grevw GPR:$rs1, 24), 16), (GREVIW GPR:$rs1, 8)>;
def : Pat<(riscv_rolw (riscv_grevw GPR:$rs1, 24), 16), (GREVIW GPR:$rs1, 8)>;
+def : PatGprGpr<riscv_grevw, GREVW>;
+def : PatGprGpr<riscv_gorcw, GORCW>;
+def : PatGprGpr<riscv_shflw, SHFLW>;
+def : PatGprGpr<riscv_unshflw, UNSHFLW>;
def : PatGprImm<riscv_grevw, GREVIW, uimm5>;
def : PatGprImm<riscv_gorcw, GORCIW, uimm5>;
} // Predicates = [HasStdExtZbp, IsRV64]
let Predicates = [HasStdExtZbbOrZbp] in {
def : InstAlias<"ror $rd, $rs1, $shamt",
                (RORI  GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
} // Predicates = [HasStdExtZbbOrZbp]

let Predicates = [HasStdExtZbbOrZbp, IsRV64] in {
def : InstAlias<"rorw $rd, $rs1, $shamt",
                (RORIW  GPR:$rd, GPR:$rs1, uimm5:$shamt), 0>;
} // Predicates = [HasStdExtZbbOrZbp, IsRV64]

let Predicates = [HasStdExtZbs] in {
def : InstAlias<"bset $rd, $rs1, $shamt",
                (BSETI  GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
def : InstAlias<"bclr $rd, $rs1, $shamt",
                (BCLRI GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
def : InstAlias<"binv $rd, $rs1, $shamt",
                (BINVI GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
def : InstAlias<"bext $rd, $rs1, $shamt",
                (BEXTI GPR:$rd, GPR:$rs1, uimmlog2xlen:$shamt), 0>;
} // Predicates = [HasStdExtZbs]

...

let Predicates = [HasStdExtZbc] in {
def : PatGprGpr<int_riscv_clmul, CLMUL>;
def : PatGprGpr<int_riscv_clmulh, CLMULH>;
def : PatGprGpr<int_riscv_clmulr, CLMULR>;
} // Predicates = [HasStdExtZbc]

let Predicates = [HasStdExtZbe] in {
def : PatGprGpr<riscv_bcompress, BCOMPRESS>;
def : PatGprGpr<riscv_bdecompress, BDECOMPRESS>;
} // Predicates = [HasStdExtZbe]

let Predicates = [HasStdExtZbe, IsRV64] in {
def : PatGprGpr<riscv_bcompressw, BCOMPRESSW>;
def : PatGprGpr<riscv_bdecompressw, BDECOMPRESSW>;
} // Predicates = [HasStdExtZbe, IsRV64]

let Predicates = [HasStdExtZbm, IsRV64] in {
def : PatGprGpr<int_riscv_bmator, BMATOR>;
def : PatGprGpr<int_riscv_bmatxor, BMATXOR>;
def : PatGpr<int_riscv_bmatflip, BMATFLIP>;
} // Predicates = [HasStdExtZbm, IsRV64]

let Predicates = [HasStdExtZbr] in {
def : PatGpr<int_riscv_crc32_b, CRC32B>;
def : PatGpr<int_riscv_crc32_h, CRC32H>;
def : PatGpr<int_riscv_crc32_w, CRC32W>;
def : PatGpr<int_riscv_crc32c_b, CRC32CB>;
def : PatGpr<int_riscv_crc32c_h, CRC32CH>;
def : PatGpr<int_riscv_crc32c_w, CRC32CW>;
} // Predicates = [HasStdExtZbr]

let Predicates = [HasStdExtZbr, IsRV64] in {
def : PatGpr<int_riscv_crc32_d, CRC32D>;
def : PatGpr<int_riscv_crc32c_d, CRC32CD>;
} // Predicates = [HasStdExtZbr, IsRV64]

Then some of *.w instruction requires sign-extendtion and truncation in SelLowering:

llvm/lib/Target/RISCV/RISCVISelLowering.h
  GREV,
  GREVW,
  GORC,
  GORCW,
  SHFL,
  SHFLW,
  UNSHFL,
  UNSHFLW,
+  // Bit Compress/Decompress implement the generic bit extract and bit deposit
+  // functions. This operation is also referred to as bit gather/scatter, bit
+  // pack/unpack, parallel extract/deposit, compress/expand, or right
+  // compress/right expand.
+  BCOMPRESS,
+  BCOMPRESSW,
+  BDECOMPRESS,
+  BDECOMPRESSW,
  // Vector Extension
  // VMV_V_X_VL matches the semantics of vmv.v.x but includes an extra operand
  // for the VL value to be used for the operation.
  VMV_V_X_VL,
  // VFMV_V_F_VL matches the semantics of vfmv.v.f but includes an extra operand
  // for the VL value to be used for the operation.
llvm/lib/Target/RISCV/RISCVISelLowering.cpp
  if (Subtarget.hasStdExtZbb() && Subtarget.is64Bit())
    setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i32, Custom);

+  if (Subtarget.hasStdExtZbe() && Subtarget.is64Bit())
+    setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i32, Custom);

  if (Subtarget.is64Bit()) {
    setOperationAction(ISD::ADD, MVT::i32, Custom);
    setOperationAction(ISD::SUB, MVT::i32, Custom);

...

    if (Subtarget.is64Bit()) {
      setOperationAction(ISD::BITREVERSE, MVT::i32, Custom);
      setOperationAction(ISD::BSWAP, MVT::i32, Custom);
+      setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::i32, Custom);
    }

...

  case Intrinsic::riscv_orc_b:
    // Lower to the GORCI encoding for orc.b.
    return DAG.getNode(RISCVISD::GORC, DL, XLenVT, Op.getOperand(1),
                       DAG.getConstant(7, DL, XLenVT));
+  case Intrinsic::riscv_grev:
+  case Intrinsic::riscv_gorc: {
+    unsigned Opc =
+        IntNo == Intrinsic::riscv_grev ? RISCVISD::GREV : RISCVISD::GORC;
+    return DAG.getNode(Opc, DL, XLenVT, Op.getOperand(1), Op.getOperand(2));
+  }
+  case Intrinsic::riscv_shfl:
+  case Intrinsic::riscv_unshfl: {
+    unsigned Opc =
+        IntNo == Intrinsic::riscv_shfl ? RISCVISD::SHFL : RISCVISD::UNSHFL;
+    return DAG.getNode(Opc, DL, XLenVT, Op.getOperand(1), Op.getOperand(2));
+  }
+  case Intrinsic::riscv_bcompress:
+  case Intrinsic::riscv_bdecompress: {
+    unsigned Opc = IntNo == Intrinsic::riscv_bcompress ? RISCVISD::BCOMPRESS
+                                                       : RISCVISD::BDECOMPRESS;
+    return DAG.getNode(Opc, DL, XLenVT, Op.getOperand(1), Op.getOperand(2));
+  }
  case Intrinsic::riscv_vmv_x_s:
    assert(Op.getValueType() == XLenVT && "Unexpected VT!");
    return DAG.getNode(RISCVISD::VMV_X_S, DL, Op.getValueType(),
                       Op.getOperand(1));

...

      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp,
                                DAG.getConstant(7, DL, MVT::i64));
      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
      return;
    }
+    case Intrinsic::riscv_grev:
+    case Intrinsic::riscv_gorc: {
+      assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+             "Unexpected custom legalisation");
+      SDValue NewOp1 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));
+      SDValue NewOp2 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(2));
+      unsigned Opc =
+          IntNo == Intrinsic::riscv_grev ? RISCVISD::GREVW : RISCVISD::GORCW;
+      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp1, NewOp2);
+      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
+      break;
+    }
+    case Intrinsic::riscv_shfl:
+    case Intrinsic::riscv_unshfl: {
+      assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+             "Unexpected custom legalisation");
+      SDValue NewOp1 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));
+      SDValue NewOp2 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(2));
+      unsigned Opc =
+          IntNo == Intrinsic::riscv_shfl ? RISCVISD::SHFLW : RISCVISD::UNSHFLW;
+      if (isa<ConstantSDNode>(N->getOperand(2))) {
+        NewOp2 = DAG.getNode(ISD::AND, DL, MVT::i64, NewOp2,
+                             DAG.getConstant(0xf, DL, MVT::i64));
+        Opc =
+            IntNo == Intrinsic::riscv_shfl ? RISCVISD::SHFL : RISCVISD::UNSHFL;
+      }
+      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp1, NewOp2);
+      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
+      break;
+    }
+    case Intrinsic::riscv_bcompress:
+    case Intrinsic::riscv_bdecompress: {
+      assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+             "Unexpected custom legalisation");
+      SDValue NewOp1 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));
+      SDValue NewOp2 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(2));
+      unsigned Opc = IntNo == Intrinsic::riscv_bcompress
+                         ? RISCVISD::BCOMPRESSW
+                         : RISCVISD::BDECOMPRESSW;
+      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp1, NewOp2);
+      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
+      break;
+    }
    case Intrinsic::riscv_vmv_x_s: {

...

            IntNo == Intrinsic::riscv_shfl ? RISCVISD::SHFL : RISCVISD::UNSHFL;
      }
      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp1, NewOp2);
      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
      break;
    }
+    case Intrinsic::riscv_bcompress:
+    case Intrinsic::riscv_bdecompress: {
+      assert(N->getValueType(0) == MVT::i32 && Subtarget.is64Bit() &&
+             "Unexpected custom legalisation");
+      SDValue NewOp1 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(1));
+      SDValue NewOp2 =
+          DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N->getOperand(2));
+      unsigned Opc = IntNo == Intrinsic::riscv_bcompress
+                         ? RISCVISD::BCOMPRESSW
+                         : RISCVISD::BDECOMPRESSW;
+      SDValue Res = DAG.getNode(Opc, DL, MVT::i64, NewOp1, NewOp2);
+      Results.push_back(DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Res));
+      break;
+    }
    case Intrinsic::riscv_vmv_x_s: {
      EVT VT = N->getValueType(0);
      MVT XLenVT = Subtarget.getXLenVT();
      if (VT.bitsLT(XLenVT)) {

...

  case RISCVISD::FSLW:
  case RISCVISD::FSRW:
+  case RISCVISD::SHFLW:
+  case RISCVISD::UNSHFLW:
+  case RISCVISD::BCOMPRESSW:
+  case RISCVISD::BDECOMPRESSW:
    // TODO: As the result is sign-extended, this is conservatively correct. A
    // more precise answer could be calculated for SRAW depending on known
    // bits in the shift amount.
    return 33;
+  case RISCVISD::SHFL:
+  case RISCVISD::UNSHFL: {
    // There is no SHFLIW, but a i64 SHFLI with bit 4 of the control word
    // cleared doesn't affect bit 31. The upper 32 bits will be shuffled, but
    // will stay within the upper 32 bits. If there were more than 32 sign bits
    // before there will be at least 33 sign bits after.
    if (Op.getValueType() == MVT::i64 &&
        isa<ConstantSDNode>(Op.getOperand(1)) &&
        (Op.getConstantOperandVal(1) & 0x10) == 0) {
      unsigned Tmp = DAG.ComputeNumSignBits(Op.getOperand(0), Depth + 1);

...

  NODE_NAME_CASE(GREVW)
  NODE_NAME_CASE(GORC)
  NODE_NAME_CASE(GORCW)
  NODE_NAME_CASE(SHFL)
+  NODE_NAME_CASE(SHFLW)
+  NODE_NAME_CASE(UNSHFL)
+  NODE_NAME_CASE(UNSHFLW)
+  NODE_NAME_CASE(BCOMPRESS)
+  NODE_NAME_CASE(BCOMPRESSW)
+  NODE_NAME_CASE(BDECOMPRESS)
+  NODE_NAME_CASE(BDECOMPRESSW)
  NODE_NAME_CASE(VMV_V_X_VL)
  NODE_NAME_CASE(VFMV_V_F_VL)


Although there is still some test cases lf if you want submit a formal patch, we’ve done most of work on backend, let’s shift to front end and check what needs to be done.

First let’s define those intrinsic in

clang/include/clang/Basic/BuiltinsRISCV.def
// Zbb extension
TARGET_BUILTIN(__builtin_riscv_orc_b_32, "ZiZi", "nc", "experimental-zbb")
TARGET_BUILTIN(__builtin_riscv_orc_b_64, "WiWi", "nc", "experimental-zbb,64bit")

// Zbc extension
TARGET_BUILTIN(__builtin_riscv_clmul, "LiLiLi", "nc", "experimental-zbc")
TARGET_BUILTIN(__builtin_riscv_clmulh, "LiLiLi", "nc", "experimental-zbc")
TARGET_BUILTIN(__builtin_riscv_clmulr, "LiLiLi", "nc", "experimental-zbc")

// Zbe extension
TARGET_BUILTIN(__builtin_riscv_bcompress_32, "ZiZiZi", "nc", "experimental-zbe")
TARGET_BUILTIN(__builtin_riscv_bcompress_64, "WiWiWi", "nc",
               "experimental-zbe,64bit")
TARGET_BUILTIN(__builtin_riscv_bdecompress_32, "ZiZiZi", "nc",
               "experimental-zbe")
TARGET_BUILTIN(__builtin_riscv_bdecompress_64, "WiWiWi", "nc",
               "experimental-zbe,64bit")

// Zbm extension
TARGET_BUILTIN(__builtin_riscv_bmator, "WiWiWi", "nc","experimental-zbm,64bit")
TARGET_BUILTIN(__builtin_riscv_bmatxor, "WiWiWi", "nc", "experimental-zbm,64bit")
TARGET_BUILTIN(__builtin_riscv_bmatflip, "WiWi", "nc", "experimental-zbm,64bit")

// Zbp extension
TARGET_BUILTIN(__builtin_riscv_grev_32, "ZiZiZi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_grev_64, "WiWiWi", "nc", "experimental-zbp,64bit")
TARGET_BUILTIN(__builtin_riscv_gorc_32, "ZiZiZi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_gorc_64, "WiWiWi", "nc", "experimental-zbp,64bit")
TARGET_BUILTIN(__builtin_riscv_shfl_32, "ZiZiZi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_shfl_64, "WiWiWi", "nc", "experimental-zbp,64bit")
TARGET_BUILTIN(__builtin_riscv_unshfl_32, "ZiZiZi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_unshfl_64, "WiWiWi", "nc", "experimental-zbp,64bit")
TARGET_BUILTIN(__builtin_riscv_xperm_n, "LiLiLi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_xperm_b, "LiLiLi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_xperm_h, "LiLiLi", "nc", "experimental-zbp")
TARGET_BUILTIN(__builtin_riscv_xperm_w, "WiWiWi", "nc", "experimental-zbp,64bit")

// Zbr extension
TARGET_BUILTIN(__builtin_riscv_crc32_b, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32_h, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32_w, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32c_b, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32c_h, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32c_w, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32_d, "LiLi", "nc", "experimental-zbr")
TARGET_BUILTIN(__builtin_riscv_crc32c_d, "LiLi", "nc", "experimental-zbr")
clang/lib/CodeGen/CGBuiltin.cpp
Value *CodeGenFunction::EmitRISCVBuiltinExpr(unsigned BuiltinID,
                                             const CallExpr *E,
                                             ReturnValueSlot ReturnValue) {
  SmallVector<Value *, 4> Ops;
  llvm::Type *ResultType = ConvertType(E->getType());

  for (unsigned i = 0, e = E->getNumArgs(); i != e; i++)
    Ops.push_back(EmitScalarExpr(E->getArg(i)));

  Intrinsic::ID ID = Intrinsic::not_intrinsic;

  // Required for overloaded intrinsics.
  llvm::SmallVector<llvm::Type *, 2> IntrinsicTypes;
  switch (BuiltinID) {
  default: llvm_unreachable("unexpected builtin ID");
  case RISCV::BI__builtin_riscv_orc_b_32:
  case RISCV::BI__builtin_riscv_orc_b_64:
  case RISCV::BI__builtin_riscv_clmul:
  case RISCV::BI__builtin_riscv_clmulh:
  case RISCV::BI__builtin_riscv_clmulr:
  case RISCV::BI__builtin_riscv_bmator:
  case RISCV::BI__builtin_riscv_bmatxor:
  case RISCV::BI__builtin_riscv_bmatflip:
  case RISCV::BI__builtin_riscv_bcompress_32:
  case RISCV::BI__builtin_riscv_bcompress_64:
  case RISCV::BI__builtin_riscv_bdecompress_32:
  case RISCV::BI__builtin_riscv_bdecompress_64:
  case RISCV::BI__builtin_riscv_grev_32:
  case RISCV::BI__builtin_riscv_grev_64:
  case RISCV::BI__builtin_riscv_gorc_32:
  case RISCV::BI__builtin_riscv_gorc_64:
  case RISCV::BI__builtin_riscv_shfl_32:
  case RISCV::BI__builtin_riscv_shfl_64:
  case RISCV::BI__builtin_riscv_unshfl_32:
  case RISCV::BI__builtin_riscv_unshfl_64:
  case RISCV::BI__builtin_riscv_xperm_n:
  case RISCV::BI__builtin_riscv_xperm_b:
  case RISCV::BI__builtin_riscv_xperm_h:
  case RISCV::BI__builtin_riscv_xperm_w:
  case RISCV::BI__builtin_riscv_crc32_b:
  case RISCV::BI__builtin_riscv_crc32_h:
  case RISCV::BI__builtin_riscv_crc32_w:
  case RISCV::BI__builtin_riscv_crc32_d:
  case RISCV::BI__builtin_riscv_crc32c_b:
  case RISCV::BI__builtin_riscv_crc32c_h:
  case RISCV::BI__builtin_riscv_crc32c_w:
  case RISCV::BI__builtin_riscv_crc32c_d: {
    switch (BuiltinID) {
    default: llvm_unreachable("unexpected builtin ID");
    // Zbb
    case RISCV::BI__builtin_riscv_orc_b_32:
    case RISCV::BI__builtin_riscv_orc_b_64:
      ID = Intrinsic::riscv_orc_b;
      break;

    // Zbc
    case RISCV::BI__builtin_riscv_clmul:
      ID = Intrinsic::riscv_clmul;
      break;
    case RISCV::BI__builtin_riscv_clmulh:
      ID = Intrinsic::riscv_clmulh;
      break;
    case RISCV::BI__builtin_riscv_clmulr:
      ID = Intrinsic::riscv_clmulr;
      break;

    // Zbe
    case RISCV::BI__builtin_riscv_bcompress_32:
    case RISCV::BI__builtin_riscv_bcompress_64:
      ID = Intrinsic::riscv_bcompress;
      break;
    case RISCV::BI__builtin_riscv_bdecompress_32:
    case RISCV::BI__builtin_riscv_bdecompress_64:
      ID = Intrinsic::riscv_bdecompress;
      break;

    // Zbm
    case RISCV::BI__builtin_riscv_bmator:
      ID = Intrinsic::riscv_bmator;
      break;
    case RISCV::BI__builtin_riscv_bmatxor:
      ID = Intrinsic::riscv_bmatxor;
      break;
    case RISCV::BI__builtin_riscv_bmatflip:
      ID = Intrinsic::riscv_bmatflip;
      break;

    // Zbp
    case RISCV::BI__builtin_riscv_grev_32:
    case RISCV::BI__builtin_riscv_grev_64:
      ID = Intrinsic::riscv_grev;
      break;
    case RISCV::BI__builtin_riscv_gorc_32:
    case RISCV::BI__builtin_riscv_gorc_64:
      ID = Intrinsic::riscv_gorc;
      break;
    case RISCV::BI__builtin_riscv_shfl_32:
    case RISCV::BI__builtin_riscv_shfl_64:
      ID = Intrinsic::riscv_shfl;
      break;
    case RISCV::BI__builtin_riscv_unshfl_32:
    case RISCV::BI__builtin_riscv_unshfl_64:
      ID = Intrinsic::riscv_unshfl;
      break;
    case RISCV::BI__builtin_riscv_xperm_n:
      ID = Intrinsic::riscv_xperm_n;
      break;
    case RISCV::BI__builtin_riscv_xperm_b:
      ID = Intrinsic::riscv_xperm_b;
      break;
    case RISCV::BI__builtin_riscv_xperm_h:
      ID = Intrinsic::riscv_xperm_h;
      break;
    case RISCV::BI__builtin_riscv_xperm_w:
      ID = Intrinsic::riscv_xperm_w;
      break;

    // Zbr
    case RISCV::BI__builtin_riscv_crc32_b:
      ID = Intrinsic::riscv_crc32_b;
      break;
    case RISCV::BI__builtin_riscv_crc32_h:
      ID = Intrinsic::riscv_crc32_h;
      break;
    case RISCV::BI__builtin_riscv_crc32_w:
      ID = Intrinsic::riscv_crc32_w;
      break;
    case RISCV::BI__builtin_riscv_crc32_d:
      ID = Intrinsic::riscv_crc32_d;
      break;
    case RISCV::BI__builtin_riscv_crc32c_b:
      ID = Intrinsic::riscv_crc32c_b;
      break;
    case RISCV::BI__builtin_riscv_crc32c_h:
      ID = Intrinsic::riscv_crc32c_h;
      break;
    case RISCV::BI__builtin_riscv_crc32c_w:
      ID = Intrinsic::riscv_crc32c_w;
      break;
    case RISCV::BI__builtin_riscv_crc32c_d:
      ID = Intrinsic::riscv_crc32c_d;
      break;
    }

    IntrinsicTypes = {ResultType};
    break;
  }
  // Vector builtins are handled from here.
#include "clang/Basic/riscv_vector_builtin_cg.inc"
  }

  assert(ID != Intrinsic::not_intrinsic);

  llvm::Function *F = CGM.getIntrinsic(ID, IntrinsicTypes);
  return Builder.CreateCall(F, Ops, "");
}

Now you should have _builtin_riscv_* to use. If additional header is needed, please refer to [2/2] of each patch.

Leave a Reply

Your email address will not be published. Required fields are marked *