Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[llvm][mc][riscv] MC support of T-Head vector extension (xtheadvector) #84447

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

kata-ark
Copy link

@kata-ark kata-ark commented Mar 8, 2024

This PR adds the following support of the T-Head vector extension:

  1. added extension info, dependencies, and versions to RISCVISAInfo.cpp.
  2. added MC support of the assembler instructions of the t-head vector.
  3. added corresponding MC tests according to the GNU AS test file.

The t-head vector is very similar to RVV 1.0, according to the t-head vector spec, but all assembler instructions are prefixed with th.. This PR implements the MC support by creating a new decoder namespace. We are looking for an elegant method to reuse existing instructions from RISCVInstrInfoV.td automatically, without adding any TableGen symbol for the "same instructions" (only names are prefixed with th.) between RVV 1.0 and T-Head Vector.

Last week we opened a discussion on discourse, and this PR may answer some questions there. Any comments and suggestions are welcomed and appreciated!!

Copy link

github-actions bot commented Mar 8, 2024

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added backend:RISC-V mc Machine (object) code llvm:support labels Mar 8, 2024
@llvmbot
Copy link
Member

llvmbot commented Mar 8, 2024

@llvm/pr-subscribers-llvm-support
@llvm/pr-subscribers-mc

@llvm/pr-subscribers-backend-risc-v

Author: None (kata-ark)

Changes

This PR adds the following support of the T-Head vector extension:

  1. added extension info, dependencies, and versions to RISCVISAInfo.cpp.
  2. added MC support of the assembler instructions of the t-head vector.
  3. added corresponding MC tests according to the GNU AS test file.

The t-head vector is very similar to RVV 1.0, according to the t-head vector spec, but all assembler instructions are prefixed with th.. This PR implements the MC support by creating a new decoder namespace. We are looking for an elegant method to reuse existing instructions from RISCVInstrInfoV.td automatically, without adding any TableGen symbol for the "same instructions" (only names are prefixed with th.) between RVV 1.0 and T-Head Vector.

Last week we opened a discussion on discourse, and this PR may answer some questions there. Any comments and suggestions are welcomed and appreciated!!


Patch is 614.92 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84447.diff

48 Files Affected:

  • (modified) llvm/include/llvm/TargetParser/RISCVTargetParser.h (+14)
  • (modified) llvm/lib/Support/RISCVISAInfo.cpp (+28-5)
  • (modified) llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp (+208-42)
  • (modified) llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp (+2)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h (+1)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.cpp (+15)
  • (modified) llvm/lib/Target/RISCV/MCTargetDesc/RISCVInstPrinter.h (+2)
  • (modified) llvm/lib/Target/RISCV/RISCVFeatures.td (+19)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.cpp (+1)
  • (modified) llvm/lib/Target/RISCV/RISCVInstrInfo.td (+1)
  • (added) llvm/lib/Target/RISCV/RISCVInstrInfoXTHeadV.td (+967)
  • (modified) llvm/lib/TargetParser/RISCVTargetParser.cpp (+28)
  • (modified) llvm/test/MC/RISCV/attribute-arch.s (+12)
  • (added) llvm/test/MC/RISCV/xtheadvector/add.s (+309)
  • (added) llvm/test/MC/RISCV/xtheadvector/and.s (+45)
  • (added) llvm/test/MC/RISCV/xtheadvector/clip.s (+81)
  • (added) llvm/test/MC/RISCV/xtheadvector/compare.s (+256)
  • (added) llvm/test/MC/RISCV/xtheadvector/convert.s (+186)
  • (added) llvm/test/MC/RISCV/xtheadvector/div.s (+105)
  • (added) llvm/test/MC/RISCV/xtheadvector/fadd.s (+84)
  • (added) llvm/test/MC/RISCV/xtheadvector/fcompare.s (+162)
  • (added) llvm/test/MC/RISCV/xtheadvector/fdiv.s (+48)
  • (added) llvm/test/MC/RISCV/xtheadvector/fmacc.s (+300)
  • (added) llvm/test/MC/RISCV/xtheadvector/fminmax.s (+60)
  • (added) llvm/test/MC/RISCV/xtheadvector/fmul.s (+60)
  • (added) llvm/test/MC/RISCV/xtheadvector/fmv.s (+30)
  • (added) llvm/test/MC/RISCV/xtheadvector/fothers.s (+40)
  • (added) llvm/test/MC/RISCV/xtheadvector/freduction.s (+90)
  • (added) llvm/test/MC/RISCV/xtheadvector/fsub.s (+96)
  • (added) llvm/test/MC/RISCV/xtheadvector/load.s (+345)
  • (added) llvm/test/MC/RISCV/xtheadvector/macc.s (+273)
  • (added) llvm/test/MC/RISCV/xtheadvector/mask.s (+141)
  • (added) llvm/test/MC/RISCV/xtheadvector/minmax.s (+105)
  • (added) llvm/test/MC/RISCV/xtheadvector/mul.s (+201)
  • (added) llvm/test/MC/RISCV/xtheadvector/mv.s (+39)
  • (added) llvm/test/MC/RISCV/xtheadvector/or.s (+45)
  • (added) llvm/test/MC/RISCV/xtheadvector/others.s (+141)
  • (added) llvm/test/MC/RISCV/xtheadvector/reduction.s (+135)
  • (added) llvm/test/MC/RISCV/xtheadvector/shift.s (+267)
  • (added) llvm/test/MC/RISCV/xtheadvector/sign-injection.s (+84)
  • (added) llvm/test/MC/RISCV/xtheadvector/store.s (+201)
  • (added) llvm/test/MC/RISCV/xtheadvector/sub.s (+273)
  • (added) llvm/test/MC/RISCV/xtheadvector/vector-insns.s (+6856)
  • (added) llvm/test/MC/RISCV/xtheadvector/vsetvl-invaild.s (+15)
  • (added) llvm/test/MC/RISCV/xtheadvector/vsetvl.s (+118)
  • (added) llvm/test/MC/RISCV/xtheadvector/xor.s (+45)
  • (added) llvm/test/MC/RISCV/xtheadvector/zvlsseg.s (+3370)
  • (modified) llvm/unittests/Support/RISCVISAInfoTest.cpp (+8)
diff --git a/llvm/include/llvm/TargetParser/RISCVTargetParser.h b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
index cdd19189f8dc7d..36ec58ea3469c1 100644
--- a/llvm/include/llvm/TargetParser/RISCVTargetParser.h
+++ b/llvm/include/llvm/TargetParser/RISCVTargetParser.h
@@ -69,9 +69,16 @@ inline static bool isValidLMUL(unsigned LMUL, bool Fractional) {
   return isPowerOf2_32(LMUL) && LMUL <= 8 && (!Fractional || LMUL != 1);
 }
 
+// Is this a EDIV value that can be encoded into the VTYPE format.
+inline static bool isValidEDIV(unsigned EDIV) {
+  return isPowerOf2_32(EDIV) && EDIV <= 8;
+}
+
 unsigned encodeVTYPE(RISCVII::VLMUL VLMUL, unsigned SEW, bool TailAgnostic,
                      bool MaskAgnostic);
 
+unsigned encodeXTHeadVTYPE(unsigned SEW, unsigned LMUL, unsigned EDIV);
+
 inline static RISCVII::VLMUL getVLMUL(unsigned VType) {
   unsigned VLMUL = VType & 0x7;
   return static_cast<RISCVII::VLMUL>(VLMUL);
@@ -101,12 +108,19 @@ inline static unsigned getSEW(unsigned VType) {
   return decodeVSEW(VSEW);
 }
 
+inline static unsigned encodeEDIV(unsigned EDIV) {
+  assert(isValidEDIV(EDIV) && "Unexpected EDIV value");
+  return Log2_32(EDIV);
+}
+
 inline static bool isTailAgnostic(unsigned VType) { return VType & 0x40; }
 
 inline static bool isMaskAgnostic(unsigned VType) { return VType & 0x80; }
 
 void printVType(unsigned VType, raw_ostream &OS);
 
+void printXTHeadVType(unsigned VType, raw_ostream &OS);
+
 unsigned getSEWLMULRatio(unsigned SEW, RISCVII::VLMUL VLMul);
 
 std::optional<RISCVII::VLMUL>
diff --git a/llvm/lib/Support/RISCVISAInfo.cpp b/llvm/lib/Support/RISCVISAInfo.cpp
index 6eec03fd6f7082..f8f8b152c20eca 100644
--- a/llvm/lib/Support/RISCVISAInfo.cpp
+++ b/llvm/lib/Support/RISCVISAInfo.cpp
@@ -106,6 +106,8 @@ static const RISCVSupportedExtension SupportedExtensions[] = {
     {"xtheadmempair", {1, 0}},
     {"xtheadsync", {1, 0}},
     {"xtheadvdot", {1, 0}},
+    {"xtheadvector", {1, 0}},
+    {"xtheadzvamo", {1, 0}},
     {"xventanacondops", {1, 0}},
 
     {"za128rs", {1, 0}},
@@ -368,6 +370,18 @@ static StringRef getExtensionType(StringRef Ext) {
   return StringRef();
 }
 
+static const RISCVSupportedExtension *
+findExtensionIn(llvm::ArrayRef<RISCVSupportedExtension> ExtensionInfos,
+                StringRef Ext, unsigned MajorVersion, unsigned MinorVersion) {
+  auto Range = std::equal_range(ExtensionInfos.begin(), ExtensionInfos.end(),
+                                Ext, LessExtName());
+  for (auto I = Range.first, E = Range.second; I != E; ++I)
+    if (I->Version.Major == MajorVersion && I->Version.Minor == MinorVersion) {
+      return I;
+    }
+  return ExtensionInfos.end();
+}
+
 static std::optional<RISCVISAInfo::ExtensionVersion>
 isExperimentalExtension(StringRef Ext) {
   auto I =
@@ -406,11 +420,8 @@ bool RISCVISAInfo::isSupportedExtension(StringRef Ext, unsigned MajorVersion,
                                         unsigned MinorVersion) {
   for (auto ExtInfo : {ArrayRef(SupportedExtensions),
                        ArrayRef(SupportedExperimentalExtensions)}) {
-    auto Range =
-        std::equal_range(ExtInfo.begin(), ExtInfo.end(), Ext, LessExtName());
-    for (auto I = Range.first, E = Range.second; I != E; ++I)
-      if (I->Version.Major == MajorVersion && I->Version.Minor == MinorVersion)
-        return true;
+    return findExtensionIn(ExtInfo, Ext, MajorVersion, MinorVersion) !=
+           ExtInfo.end();
   }
 
   return false;
@@ -1038,6 +1049,16 @@ Error RISCVISAInfo::checkDependency() {
     return createStringError(errc::invalid_argument,
                              "'zcf' is only supported for 'rv32'");
 
+  if (Exts.count("xtheadzvamo") && !Exts.count("a"))
+    return createStringError(
+        errc::invalid_argument,
+        "'xtheadzvamo' requires 'a' extension to also be specified");
+
+  if (Exts.count("xtheadvector") && HasVector)
+    return createStringError(
+        errc::invalid_argument,
+        "'xtheadvector' extension is incompatible with 'v' or 'zve*' extension");
+
   return Error::success();
 }
 
@@ -1045,6 +1066,7 @@ static const char *ImpliedExtsD[] = {"f"};
 static const char *ImpliedExtsF[] = {"zicsr"};
 static const char *ImpliedExtsV[] = {"zvl128b", "zve64d"};
 static const char *ImpliedExtsXTHeadVdot[] = {"v"};
+static const char *ImpliedExtsXTHeadZvamo[] = {"a"};
 static const char *ImpliedExtsXSfvcp[] = {"zve32x"};
 static const char *ImpliedExtsXSfvfnrclipxfqf[] = {"zve32f"};
 static const char *ImpliedExtsXSfvfwmaccqqq[] = {"zvfbfmin"};
@@ -1125,6 +1147,7 @@ static constexpr ImpliedExtsEntry ImpliedExts[] = {
     {{"xsfvqmaccdod"}, {ImpliedExtsXSfvqmaccdod}},
     {{"xsfvqmaccqoq"}, {ImpliedExtsXSfvqmaccqoq}},
     {{"xtheadvdot"}, {ImpliedExtsXTHeadVdot}},
+    {{"xtheadzvamo"}, {ImpliedExtsXTHeadZvamo}},
     {{"zabha"}, {ImpliedExtsZabha}},
     {{"zacas"}, {ImpliedExtsZacas}},
     {{"zcb"}, {ImpliedExtsZcb}},
diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index d83979a873f2a3..32d08cfeafe2cd 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -163,6 +163,13 @@ class RISCVAsmParser : public MCTargetAsmParser {
   // Helper to emit pseudo vmsge{u}.vx instruction.
   void emitVMSGE(MCInst &Inst, unsigned Opcode, SMLoc IDLoc, MCStreamer &Out);
 
+  // Helper to emit pseudo vmsge{u}.vi instruction.
+  void emitVMSGE_VI(MCInst &Inst, unsigned Opcode, unsigned OpcodeImmIs0,
+                    SMLoc IDLoc, MCStreamer &Out, bool IsSigned);
+
+  // Helper to emit pseudo vmsge{u}.vx instruction for XTHeadV extension.
+  void emitVMSGE_TH(MCInst &Inst, unsigned Opcode, SMLoc IDLoc, MCStreamer &Out);
+
   // Checks that a PseudoAddTPRel is using x4/tp in its second input operand.
   // Enforcing this using a restricted register class for the second input
   // operand of PseudoAddTPRel results in a poor diagnostic due to the fact
@@ -201,6 +208,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   ParseStatus parsePseudoJumpSymbol(OperandVector &Operands);
   ParseStatus parseJALOffset(OperandVector &Operands);
   ParseStatus parseVTypeI(OperandVector &Operands);
+  ParseStatus parseXTHeadVTypeI(OperandVector &Operands);
   ParseStatus parseMaskReg(OperandVector &Operands);
   ParseStatus parseInsnDirectiveOpcode(OperandVector &Operands);
   ParseStatus parseInsnCDirectiveOpcode(OperandVector &Operands);
@@ -329,6 +337,7 @@ struct RISCVOperand final : public MCParsedAsmOperand {
     FPImmediate,
     SystemRegister,
     VType,
+    XTHeadVType,
     FRM,
     Fence,
     Rlist,
@@ -422,6 +431,7 @@ struct RISCVOperand final : public MCParsedAsmOperand {
       SysReg = o.SysReg;
       break;
     case KindTy::VType:
+    case KindTy::XTHeadVType:
       VType = o.VType;
       break;
     case KindTy::FRM:
@@ -588,6 +598,11 @@ struct RISCVOperand final : public MCParsedAsmOperand {
       return isVTypeImm(11);
     return Kind == KindTy::VType;
   }
+  bool isXTHeadVTypeI() const {
+    if (Kind == KindTy::Immediate)
+      return isVTypeImm(11);
+    return Kind == KindTy::XTHeadVType;
+  }
 
   /// Return true if the operand is a valid for the fence instruction e.g.
   /// ('iorw').
@@ -1002,7 +1017,8 @@ struct RISCVOperand final : public MCParsedAsmOperand {
   }
 
   unsigned getVType() const {
-    assert(Kind == KindTy::VType && "Invalid type access!");
+    assert((Kind == KindTy::VType || Kind == KindTy::XTHeadVType) &&
+           "Invalid type access!");
     return VType.Val;
   }
 
@@ -1044,6 +1060,11 @@ struct RISCVOperand final : public MCParsedAsmOperand {
       RISCVVType::printVType(getVType(), OS);
       OS << '>';
       break;
+    case KindTy::XTHeadVType:
+      OS << "<vtype: ";
+      RISCVVType::printXTHeadVType(getVType(), OS);
+      OS << '>';
+      break;
     case KindTy::FRM:
       OS << "<frm: ";
       roundingModeToString(getFRM());
@@ -1168,6 +1189,14 @@ struct RISCVOperand final : public MCParsedAsmOperand {
     return Op;
   }
 
+  static std::unique_ptr<RISCVOperand> createXTHeadVType(unsigned VTypeI, SMLoc S) {
+    auto Op = std::make_unique<RISCVOperand>(KindTy::XTHeadVType);
+    Op->VType.Val = VTypeI;
+    Op->StartLoc = S;
+    Op->EndLoc = S;
+    return Op;
+  }
+
   static void addExpr(MCInst &Inst, const MCExpr *Expr, bool IsRV64Imm) {
     assert(Expr && "Expr shouldn't be null!");
     int64_t Imm = 0;
@@ -1592,6 +1621,13 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
     SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc();
     return generateVTypeError(ErrorLoc);
   }
+  case Match_InvalidXTHeadVTypeI: {
+    SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc();
+    return Error(
+        ErrorLoc,
+        "operand must be "
+        "e[8|16|32|64|128|256|512|1024],m[1|2|4|8],d[1|2|4|8] for XTHeadVector");
+  }
   case Match_InvalidVMaskRegister: {
     SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc();
     return Error(ErrorLoc, "operand must be v0.t");
@@ -2225,6 +2261,57 @@ bool RISCVAsmParser::generateVTypeError(SMLoc ErrorLoc) {
       "e[8|16|32|64|128|256|512|1024],m[1|2|4|8|f2|f4|f8],[ta|tu],[ma|mu]");
 }
 
+ParseStatus RISCVAsmParser::parseXTHeadVTypeI(OperandVector &Operands) {
+  SMLoc S = getLoc();
+  if (getLexer().isNot(AsmToken::Identifier))
+    return ParseStatus::NoMatch;
+
+  SmallVector<AsmToken, 7> VTypeIElements;
+  auto MatchFail = [&]() {
+    while (!VTypeIElements.empty())
+      getLexer().UnLex(VTypeIElements.pop_back_val());
+    return ParseStatus::NoMatch;
+  };
+
+  // Put all the tokens for vtypei operand into VTypeIElements vector.
+  while (getLexer().isNot(AsmToken::EndOfStatement)) {
+    VTypeIElements.push_back(getLexer().getTok());
+    getLexer().Lex();
+    if (getLexer().is(AsmToken::EndOfStatement))
+      break;
+    if (getLexer().isNot(AsmToken::Comma))
+      return MatchFail();
+    AsmToken Comma = getLexer().getTok();
+    VTypeIElements.push_back(Comma);
+    getLexer().Lex();
+  }
+
+  if (VTypeIElements.size() == 5) {
+    // The VTypeIElements layout is:
+    // SEW comma LMUL comma EDIV
+    //  0    1    2     3    4
+    StringRef Prefix[] = {"e", "m", "d"};
+    unsigned VTypeEle[3];
+    for (size_t i = 0; i < 3; ++i) {
+      StringRef Name = VTypeIElements[2 * i].getIdentifier();
+      if (!Name.consume_front(Prefix[i]) || Name.getAsInteger(10, VTypeEle[i]))
+        return MatchFail();
+    }
+
+    if (!RISCVVType::isValidSEW(VTypeEle[0]) ||
+        !RISCVVType::isValidLMUL(VTypeEle[1], false) ||
+        !RISCVVType::isValidEDIV(VTypeEle[2]))
+      return MatchFail();
+
+    unsigned VTypeI =
+        RISCVVType::encodeXTHeadVTYPE(VTypeEle[0], VTypeEle[1], VTypeEle[2]);
+    Operands.push_back(RISCVOperand::createXTHeadVType(VTypeI, S));
+    return ParseStatus::Success;
+  }
+
+  return MatchFail();
+}
+
 ParseStatus RISCVAsmParser::parseMaskReg(OperandVector &Operands) {
   if (getLexer().isNot(AsmToken::Identifier))
     return ParseStatus::NoMatch;
@@ -3339,6 +3426,92 @@ void RISCVAsmParser::emitVMSGE(MCInst &Inst, unsigned Opcode, SMLoc IDLoc,
   }
 }
 
+
+void RISCVAsmParser::emitVMSGE_VI(MCInst &Inst, unsigned Opcode,
+                                  unsigned OpcodeImmIs0, SMLoc IDLoc,
+                                  MCStreamer &Out, bool IsSigned) {
+  int64_t Imm = Inst.getOperand(2).getImm();
+  if (IsSigned) {
+    // These instructions are signed and so is immediate so we can subtract one.
+    emitToStreamer(Out, MCInstBuilder(Opcode)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(1))
+                            .addImm(Imm - 1)
+                            .addOperand(Inst.getOperand(3)));
+  } else {
+    // Unsigned comparisons are tricky because the immediate is signed. If the
+    // immediate is 0 we can't just subtract one. vmsltu.vi v0, v1, 0 is always
+    // false, but vmsle.vi v0, v1, -1 is always true. Instead we use
+    // vmsne v0, v1, v1 which is always false.
+    if (Imm == 0) {
+      emitToStreamer(Out, MCInstBuilder(OpcodeImmIs0)
+                              .addOperand(Inst.getOperand(0))
+                              .addOperand(Inst.getOperand(1))
+                              .addOperand(Inst.getOperand(1))
+                              .addOperand(Inst.getOperand(3)));
+    } else {
+      // Other immediate values can subtract one like signed.
+      emitToStreamer(Out, MCInstBuilder(Opcode)
+                              .addOperand(Inst.getOperand(0))
+                              .addOperand(Inst.getOperand(1))
+                              .addImm(Imm - 1)
+                              .addOperand(Inst.getOperand(3)));
+    }
+  }
+}
+
+void RISCVAsmParser::emitVMSGE_TH(MCInst &Inst, unsigned Opcode, SMLoc IDLoc,
+                                MCStreamer &Out) {
+  // https://github.com/riscv/riscv-v-spec/releases/tag/0.7.1
+  if (Inst.getNumOperands() == 3) {
+    // unmasked va >= x
+    //
+    //  pseudoinstruction: vmsge{u}.vx vd, va, x
+    //  expansion: vmslt{u}.vx vd, va, x; vmnand.mm vd, vd, vd
+    emitToStreamer(Out, MCInstBuilder(Opcode)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(1))
+                            .addOperand(Inst.getOperand(2))
+                            .addReg(RISCV::NoRegister));
+    emitToStreamer(Out, MCInstBuilder(RISCV::TH_VMNAND_MM)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(0)));
+  } else if (Inst.getNumOperands() == 4) {
+    // masked va >= x, vd != v0
+    //
+    //  pseudoinstruction: vmsge{u}.vx vd, va, x, v0.t
+    //  expansion: vmslt{u}.vx vd, va, x, v0.t; vmxor.mm vd, vd, v0
+    assert(Inst.getOperand(0).getReg() != RISCV::V0 &&
+           "The destination register should not be V0.");
+    emitToStreamer(Out, MCInstBuilder(Opcode)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(1))
+                            .addOperand(Inst.getOperand(2))
+                            .addOperand(Inst.getOperand(3)));
+    emitToStreamer(Out, MCInstBuilder(RISCV::TH_VMXOR_MM)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(0))
+                            .addReg(RISCV::V0));
+  } else if (Inst.getNumOperands() == 5) {
+    // masked va >= x, any vd
+    //
+    // pseudoinstruction: vmsge{u}.vx vd, va, x, v0.t, vt
+    // expansion: vmslt{u}.vx vt, va, x; vmandnot.mm vd, vd, vt
+    assert(Inst.getOperand(1).getReg() != RISCV::V0 &&
+           "The temporary vector register should not be V0.");
+    emitToStreamer(Out, MCInstBuilder(Opcode)
+                            .addOperand(Inst.getOperand(1))
+                            .addOperand(Inst.getOperand(2))
+                            .addOperand(Inst.getOperand(3))
+                            .addReg(RISCV::NoRegister));
+    emitToStreamer(Out, MCInstBuilder(RISCV::TH_VMANDN_MM)
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(0))
+                            .addOperand(Inst.getOperand(1)));
+  }
+}
+
 bool RISCVAsmParser::checkPseudoAddTPRel(MCInst &Inst,
                                          OperandVector &Operands) {
   assert(Inst.getOpcode() == RISCV::PseudoAddTPRel && "Invalid instruction");
@@ -3385,7 +3558,9 @@ bool RISCVAsmParser::validateInstruction(MCInst &Inst,
   unsigned Opcode = Inst.getOpcode();
 
   if (Opcode == RISCV::PseudoVMSGEU_VX_M_T ||
-      Opcode == RISCV::PseudoVMSGE_VX_M_T) {
+      Opcode == RISCV::PseudoVMSGE_VX_M_T ||
+      Opcode == RISCV::PseudoTH_VMSGEU_VX_M_T ||
+      Opcode == RISCV::PseudoTH_VMSGE_VX_M_T) {
     unsigned DestReg = Inst.getOperand(0).getReg();
     unsigned TempReg = Inst.getOperand(1).getReg();
     if (DestReg == TempReg) {
@@ -3627,49 +3802,40 @@ bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
     emitVMSGE(Inst, RISCV::VMSLT_VX, IDLoc, Out);
     return false;
   case RISCV::PseudoVMSGE_VI:
-  case RISCV::PseudoVMSLT_VI: {
-    // These instructions are signed and so is immediate so we can subtract one
-    // and change the opcode.
-    int64_t Imm = Inst.getOperand(2).getImm();
-    unsigned Opc = Inst.getOpcode() == RISCV::PseudoVMSGE_VI ? RISCV::VMSGT_VI
-                                                             : RISCV::VMSLE_VI;
-    emitToStreamer(Out, MCInstBuilder(Opc)
-                            .addOperand(Inst.getOperand(0))
-                            .addOperand(Inst.getOperand(1))
-                            .addImm(Imm - 1)
-                            .addOperand(Inst.getOperand(3)));
+    emitVMSGE_VI(Inst, RISCV::VMSGT_VI, RISCV::VMSGT_VI, IDLoc, Out, true);
+    return false;
+  case RISCV::PseudoVMSLT_VI:
+    emitVMSGE_VI(Inst, RISCV::VMSLE_VI, RISCV::VMSLE_VI, IDLoc, Out, true);
     return false;
-  }
   case RISCV::PseudoVMSGEU_VI:
-  case RISCV::PseudoVMSLTU_VI: {
-    int64_t Imm = Inst.getOperand(2).getImm();
-    // Unsigned comparisons are tricky because the immediate is signed. If the
-    // immediate is 0 we can't just subtract one. vmsltu.vi v0, v1, 0 is always
-    // false, but vmsle.vi v0, v1, -1 is always true. Instead we use
-    // vmsne v0, v1, v1 which is always false.
-    if (Imm == 0) {
-      unsigned Opc = Inst.getOpcode() == RISCV::PseudoVMSGEU_VI
-                         ? RISCV::VMSEQ_VV
-                         : RISCV::VMSNE_VV;
-      emitToStreamer(Out, MCInstBuilder(Opc)
-                              .addOperand(Inst.getOperand(0))
-                              .addOperand(Inst.getOperand(1))
-                              .addOperand(Inst.getOperand(1))
-                              .addOperand(Inst.getOperand(3)));
-    } else {
-      // Other immediate values can subtract one like signed.
-      unsigned Opc = Inst.getOpcode() == RISCV::PseudoVMSGEU_VI
-                         ? RISCV::VMSGTU_VI
-                         : RISCV::VMSLEU_VI;
-      emitToStreamer(Out, MCInstBuilder(Opc)
-                              .addOperand(Inst.getOperand(0))
-                              .addOperand(Inst.getOperand(1))
-                              .addImm(Imm - 1)
-                              .addOperand(Inst.getOperand(3)));
-    }
-
+    emitVMSGE_VI(Inst, RISCV::VMSGTU_VI, RISCV::VMSEQ_VV, IDLoc, Out, false);
+    return false;
+  case RISCV::PseudoVMSLTU_VI:
+    emitVMSGE_VI(Inst, RISCV::VMSLEU_VI, RISCV::VMSNE_VV, IDLoc, Out, false);
+    return false;
+  // for XTHeadVector Extension
+  case RISCV::PseudoTH_VMSGEU_VX:
+  case RISCV::PseudoTH_VMSGEU_VX_M:
+  case RISCV::PseudoTH_VMSGEU_VX_M_T:
+    emitVMSGE_TH(Inst, RISCV::TH_VMSLTU_VX, IDLoc, Out);
+    return false;
+  case RISCV::PseudoTH_VMSGE_VX:
+  case RISCV::PseudoTH_VMSGE_VX_M:
+  case RISCV::PseudoTH_VMSGE_VX_M_T:
+    emitVMSGE_TH(Inst, RISCV::TH_VMSLT_VX, IDLoc, Out);
+    return false;
+  case RISCV::PseudoTH_VMSGE_VI:
+    emitVMSGE_VI(Inst, RISCV::TH_VMSGT_VI, RISCV::TH_VMSGT_VI, IDLoc, Out, true);
+    return false;
+  case RISCV::PseudoTH_VMSLT_VI:
+    emitVMSGE_VI(Inst, RISCV::TH_VMSLE_VI, RISCV::TH_VMSLE_VI, IDLoc, Out, true);
+    return false;
+  case RISCV::PseudoTH_VMSGEU_VI:
+    emitVMSGE_VI(Inst, RISCV::TH_VMSGTU_VI, RISCV::TH_VMSEQ_VV, IDLoc, Out, false);
+    return false;
+  case RISCV::PseudoTH_VMSLTU_VI:
+    emitVMSGE_VI(Inst, RISCV::TH_VMSLEU_VI, RISCV::TH_VMSNE_VV, IDLoc, Out, false);
     return false;
-  }
   }
 
   emitToStreamer(Out, Inst);
diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index f1ca1212ec378e..656bd5aac47ea5 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -581,6 +581,8 @@ DecodeStatus RISCVDisassembler::getInstruction(MCInst &MI, uint64_t &Size,
                           "XTHeadSync custom opcode table");
     TRY_TO_DECODE_FEATURE(RISCV::FeatureVendorXTHeadVdot, DecoderTableXTHeadVdot32,
                           "XTHeadVdot custom opcode table");
+    TRY_T...
[truncated]

Copy link

github-actions bot commented Mar 8, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Contributor

@wangpc-pp wangpc-pp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some T-Head guys to review this.

@@ -106,6 +106,8 @@ static const RISCVSupportedExtension SupportedExtensions[] = {
{"xtheadmempair", {1, 0}},
{"xtheadsync", {1, 0}},
{"xtheadvdot", {1, 0}},
{"xtheadvector", {1, 0}},
{"xtheadzvamo", {1, 0}},
Copy link
Contributor

@wangpc-pp wangpc-pp Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if we need this. IIRC, C906/C910 didn't implement this extension. CC @zixuan-wu @rjiejie

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HI, Pengcheng,
In fact, C906/C910 support the Zvamo extension related instructions. For details here. As I have already described in the spec, it has only been renamed from Zvamo to XTheadZvamo. BTW, Here is a binutils patch that has been tested and reviewed for reference.

@@ -69,9 +69,16 @@ inline static bool isValidLMUL(unsigned LMUL, bool Fractional) {
return isPowerOf2_32(LMUL) && LMUL <= 8 && (!Fractional || LMUL != 1);
}

// Is this a EDIV value that can be encoded into the VTYPE format.
inline static bool isValidEDIV(unsigned EDIV) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC, C906/C910 didn't implement EDIV.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I also think it should not be implemented because there is no hardware to support it.

for (auto I = Range.first, E = Range.second; I != E; ++I)
if (I->Version.Major == MajorVersion && I->Version.Minor == MinorVersion)
return true;
return findExtensionIn(ExtInfo, Ext, MajorVersion, MinorVersion) !=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't belong to this PR I think.

@silvanshade
Copy link

Thanks for working on this. I plan to experiment with this on an SG2042 system soon.

I also created a PR to rebase this on main and fix the build: kata-ark#1

@imkiva
Copy link
Member

imkiva commented Apr 16, 2024

Thanks for working on this. I plan to experiment with this on an SG2042 system soon.

I also created a PR to rebase this on main and fix the build: kata-ark#1

Oh, There's a more complete but draft version with more intrinsics, rather than only MC instructions, on https://github.com/ruyisdk/llvm-project which is based on LLVM 17.0.6 (actually, this PR is "cherry-picked" from that downstream repo). Maybe worth a try? 😉

@asb
Copy link
Contributor

asb commented Apr 16, 2024

Hi @kata-ark, welcome to the LLVM project and thank you for posting your first PR.

I'm afraid I have to raise a pretty important issue here - you note that you've directly ported a gas test case as part of your work. Due to LLVM's copyright and license policy this isn't something that can be accepted due to GNU as source code and tests being under a different license. Please create original MC layer tests that are not adapted/copied from GNU.

@asb
Copy link
Contributor

asb commented Apr 16, 2024

Last week we opened a discussion on discourse, and this PR may answer some questions there. Any comments and suggestions are welcomed and appreciated!!

It does help with that, thank you. Given we started discussion there it would probably be best to try to keep discussion around the policy question of taking in patches supporting this extension in that thread, so discussion doesn't get spread out all over the place. I've fed back a question that came up in the last RISC-V LLVM sync-up call there.

Copy link
Contributor

@asb asb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment re source of test files.

@kata-ark
Copy link
Author

Hi @kata-ark, welcome to the LLVM project and thank you for posting your first PR.

I'm afraid I have to raise a pretty important issue here - you note that you've directly ported a gas test case as part of your work. Due to LLVM's copyright and license policy this isn't something that can be accepted due to GNU as source code and tests being under a different license. Please create original MC layer tests that are not adapted/copied from GNU.

Thanks for your reply. I will delete that test.

But I have to clarify that, by "ported" I mean:

  • I first converted the GNU AS test source to a normal assembly source file (using some shell commands)
  • and then compiled the GNU AS test source using LLVM assembler (to test this MC layer),
  • then decompiled it to assembly source code (to test the disassembler and pretty-printer),
  • then, I wrote a script to compare the decompiled source with the GNU AS test source to see if there's a mismatch,
  • finally, pretty-print the decompiled source to LLVM test format, which is available here.

I am not sure if decompiling a program that is compiled from a source file licensed under the GPL violates LLVM's copyright and license policy or general GPL rules. But to avoid misunderstanding, I will still delete this test. As the remaining tests can already cover the new assembly instructions very well. The test from GNU AS is added just to ensure the consistency of behavior between LLVM and GCC.

@kata-ark
Copy link
Author

Last week we opened a discussion on discourse, and this PR may answer some questions there. Any comments and suggestions are welcomed and appreciated!!

It does help with that, thank you. Given we started discussion there it would probably be best to try to keep discussion around the policy question of taking in patches supporting this extension in that thread, so discussion doesn't get spread out all over the place. I've fed back a question that came up in the last RISC-V LLVM sync-up call there.

Okay, let's move the discussion to where it started. 😉

//
//===----------------------------------------------------------------------===//
///
/// This file describes the RISC-V instructions from the standard 'V' Vector
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please clarify this comment is for the T-Head implementation since all instruction names have th. prefix it is not the standard extension.

def : InstAlias<"th.vmmv.m $vd, $vs",
(TH_VMAND_MM VR:$vd, VR:$vs, VR:$vs), 0>;
def : InstAlias<"th.vneg.v $vd, $vs",
(TH_VRSUB_VX VR:$vd, VR:$vs, X0, zero_reg)>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no th.vneg.v alias with VMaskOp:$vm? We have one for the V extension.

def : InstAlias<"th.vncvt.x.x.v $vd, $vs$vm",
(TH_VNSRL_VX VR:$vd, VR:$vs, X0, VMaskOp:$vm)>;
def : InstAlias<"th.vfneg.v $vd, $vs",
(TH_VFSGNJN_VV VR:$vd, VR:$vs, VR:$vs, zero_reg)>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no th.vfneg.v alias with VMaskOp:$vm? We have one for the V extension.

(TH_VNSRL_VX VR:$vd, VR:$vs, X0, VMaskOp:$vm)>;
def : InstAlias<"th.vfneg.v $vd, $vs",
(TH_VFSGNJN_VV VR:$vd, VR:$vs, VR:$vs, zero_reg)>;
def : InstAlias<"th.vfabs.v $vd, $vs",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no th.vfabs.v alias with VMaskOp:$vm? We have one for the V extension.

let RVVConstraint = NoConstraint in {
defm TH_VMAND_M : VMALU_MV_Mask<"th.vmand", 0b011001, "m">;
defm TH_VMNAND_M : VMALU_MV_Mask<"th.vmnand", 0b011101, "m">;
defm TH_VMANDN_M : VMALU_MV_Mask<"th.vmandnot", 0b011000, "m">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be TH_VMANDNOT_M to match the mnemonic

let RVVConstraint = NoConstraint in {
let Uses = [FRM], mayRaiseFPException = true in {
defm TH_VFREDOSUM : VREDO_FV_V<"th.vfredosum", 0b000011>;
defm TH_VFREDUSUM : VRED_FV_V<"th.vfredsum", 0b000001>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be TH_VFREDSUM to match the mnemonic

}

let hasSideEffects = 0, mayLoad = 1, mayStore = 1 in {
// vamo vd, vs2, (rs1), vd, vm
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this syntax only documented in the 0.8 spec and not the 0.7.1 spec?

@lattner
Copy link
Collaborator

lattner commented Apr 19, 2024

Super awesome @kata-ark thank you for doing this. I'm sorry for the back and forth on this, but it is very important to LLVM to make sure to protect the rights of people who are innovating and creating things, which means we have to be careful about derivative works. I am very happy you're pushing forward here and are trying to make LLVM better, I'm sorry it cost you a bit of time, but this sort of thing helps make LLVM stronger in the long run. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants