diff --git a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h index 7628987b9587d..04fa123b4d2a2 100644 --- a/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h +++ b/llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h @@ -353,6 +353,14 @@ class LLVM_ABI MCTargetAsmParser : public MCAsmParserExtension { /// AvailableFeatures - The current set of available features. FeatureBitset AvailableFeatures; + /// A set of features that conflict with each other. This helps improve + /// diagnostics by ignoring instruction variants that are mutually exclusive. + /// An example here are the CHERI mode-dependent instructions where we have + /// loads/stores that are identical other than the register class for the base + /// register and have predicates that can never both be set. To avoid + /// nonsensical error messages, we should only use the candidate instructions + /// with the currently available feature bits. + FeatureBitset ConflictingFeatures; /// ParsingMSInlineAsm - Are we parsing ms-style inline assembly? bool ParsingMSInlineAsm = false; @@ -384,6 +392,15 @@ class LLVM_ABI MCTargetAsmParser : public MCAsmParserExtension { AvailableFeatures = Value; } + const FeatureBitset &getConflictingFeatures() const { + return ConflictingFeatures; + } + void setConflictingFeatures(const FeatureBitset &Value) { + assert((Value.none() || Value.count() == 2) && + "Only zero or two conflicting features supported"); + ConflictingFeatures = Value; + } + bool isParsingMSInlineAsm () { return ParsingMSInlineAsm; } void setParsingMSInlineAsm (bool Value) { ParsingMSInlineAsm = Value; } diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 21dbb7cbc9844..cd1fa5f7236bc 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -307,6 +307,15 @@ class RISCVAsmParser : public MCTargetAsmParser { Parser.addAliasForDirective(".word", ".4byte"); Parser.addAliasForDirective(".dword", ".8byte"); setAvailableFeatures(ComputeAvailableFeatures(STI.getFeatureBits())); + // Don't record errors based on the CapMode feature. XOR the two + // bit patterns to get something that has just CapMode and NotCapMode. + // Skipping mnemonics that are not available in the current assembler mode + // significantly improves diagnostics and allows reusing the same register + // names for capmode vs non-capmode. + FeatureBitset CapModeSet = + ComputeAvailableFeatures({RISCV::FeatureCapMode}); + FeatureBitset NoCapModeSet = ComputeAvailableFeatures({}); + // setConflictingFeatures(CapModeSet ^ NoCapModeSet); auto ABIName = StringRef(Options.ABIName); if (ABIName.ends_with("f") && !getSTI().hasFeature(RISCV::FeatureStdExtF)) { @@ -491,6 +500,11 @@ struct RISCVOperand final : public MCParsedAsmOperand { RISCVMCRegisterClasses[RISCV::GPRRegClassID].contains(Reg.RegNum); } + bool isYGPR() const { + return Kind == KindTy::Register && + RISCVMCRegisterClasses[RISCV::YGPRRegClassID].contains(Reg.RegNum); + } + bool isGPRPair() const { return Kind == KindTy::Register && RISCVMCRegisterClasses[RISCV::GPRPairRegClassID].contains( @@ -767,6 +781,11 @@ struct RISCVOperand final : public MCParsedAsmOperand { }); } + bool isUImm7Srliy() const { + return isUImmPred( + [this](int64_t Imm) { return isRV64Expr() ? Imm == 64 : Imm == 32; }); + } + bool isUImm8GE32() const { return isUImmPred([](int64_t Imm) { return isUInt<8>(Imm) && Imm >= 32; }); } @@ -799,6 +818,29 @@ struct RISCVOperand final : public MCParsedAsmOperand { return IsConstantImm && isInt(fixImmediateForRV32(Imm, isRV64Expr())); } + bool isYBNDSWImm() const { + if (!isExpr()) + return false; + + int64_t Imm; + bool IsConstantImm = evaluateConstantExpr(getExpr(), Imm); + if (!IsConstantImm) + return false; + // The immediate is encoded using `((imm[7:0] + 257) << imm[9:8]) - 256` + // which gives the following valid ranges: + if (Imm < 1) + return false; + if (Imm <= 256) + return true; + if (Imm <= 768) + return (Imm % 2) == 0; + if (Imm <= 1792) + return (Imm % 4) == 0; + if (Imm <= 3840) + return (Imm % 8) == 0; + return false; + } + template bool isSImmPred(Pred p) const { int64_t Imm; if (!isExpr()) @@ -1306,6 +1348,11 @@ static MCRegister convertFPR64ToFPR128(MCRegister Reg) { return Reg - RISCV::F0_D + RISCV::F0_Q; } +static MCRegister convertGPRToYGPR(MCRegister Reg) { + assert(Reg >= RISCV::X0 && Reg <= RISCV::X31 && "Invalid register"); + return Reg - RISCV::X0 + RISCV::X0_Y; +} + static MCRegister convertVRToVRMx(const MCRegisterInfo &RI, MCRegister Reg, unsigned Kind) { unsigned RegClassID; @@ -1333,7 +1380,11 @@ unsigned RISCVAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, bool IsRegFPR64C = RISCVMCRegisterClasses[RISCV::FPR64CRegClassID].contains(Reg); bool IsRegVR = RISCVMCRegisterClasses[RISCV::VRRegClassID].contains(Reg); - + if (Op.isGPR() && Kind == MCK_YGPR) { + // GPR and capability GPR use the same register names, convert if required. + Op.Reg.RegNum = convertGPRToYGPR(Reg); + return Match_Success; + } if (IsRegFPR64 && Kind == MCK_FPR128) { Op.Reg.RegNum = convertFPR64ToFPR128(Reg); return Match_Success; @@ -1708,6 +1759,18 @@ bool RISCVAsmParser::matchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, ErrorLoc, "stack adjustment is invalid for this instruction and register list"); } + case Match_InvalidYBNDSWImm: { + const SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "immediate must be an integer in the range " + "[1, 256], a multiple of 2 in the range [258, 768], " + "a multiple of 4 in the range [772, 1792], or " + "a multiple of 8 in the range [1800, 3840]"); + } + case Match_InvalidUImm7Srliy: { + const SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "immediate must be an integer equal to XLEN (" + + Twine(isRV64() ? "64" : "32") + ")"); + } } if (const char *MatchDiag = getMatchKindDiag((RISCVMatchResultTy)Result)) { @@ -3483,6 +3546,8 @@ void RISCVAsmParser::emitToStreamer(MCStreamer &S, const MCInst &Inst) { Res = RISCVRVC::compress(CInst, Inst, STI); if (Res) ++RISCVNumInstrsCompressed; + // This catches the broken CHERI expansions + RISCV_MC::verifyInstructionPredicates(Inst.getOpcode(), STI.getFeatureBits()); S.emitInstruction((Res ? CInst : Inst), STI); } diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index b8ec0bbfcd3bb..1b93f5beaf2c9 100644 --- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp +++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -91,6 +91,19 @@ static DecodeStatus DecodeGPRRegisterClass(MCInst &Inst, uint32_t RegNo, return MCDisassembler::Success; } +static DecodeStatus DecodeYGPRRegisterClass(MCInst &Inst, uint32_t RegNo, + uint64_t Address, + const MCDisassembler *Decoder) { + bool IsRVE = Decoder->getSubtargetInfo().hasFeature(RISCV::FeatureStdExtE); + + if (RegNo >= 32 || (IsRVE && RegNo >= 16)) + return MCDisassembler::Fail; + + MCRegister Reg = RISCV::X0_Y + RegNo; + Inst.addOperand(MCOperand::createReg(Reg)); + return MCDisassembler::Success; +} + static DecodeStatus DecodeGPRF16RegisterClass(MCInst &Inst, uint32_t RegNo, uint64_t Address, const MCDisassembler *Decoder) { @@ -416,6 +429,15 @@ static DecodeStatus DecodeTRM4RegisterClass(MCInst &Inst, uint32_t RegNo, return MCDisassembler::Success; } +static DecodeStatus DecodeYBNDSWImm(MCInst &Inst, uint64_t Imm, int64_t Address, + const MCDisassembler *Decoder) { + assert(isUInt<10>(Imm) && "Invalid immediate"); + const uint32_t Shift = Imm >> 8; + uint64_t Result = (((Imm & maxUIntN(8)) + 257) << Shift) - 256; + Inst.addOperand(MCOperand::createImm(Result)); + return MCDisassembler::Success; +} + static DecodeStatus decodeVMaskReg(MCInst &Inst, uint32_t RegNo, uint64_t Address, const MCDisassembler *Decoder) { @@ -497,6 +519,20 @@ static DecodeStatus decodeUImmLog2XLenOperand(MCInst &Inst, uint32_t Imm, return MCDisassembler::Success; } +static DecodeStatus decodeUImm7SrliyOperand(MCInst &Inst, uint32_t Imm, + int64_t Address, + const MCDisassembler *Decoder) { + assert(isUInt<7>(Imm) && "Invalid immediate"); + + uint32_t ExpectedValue = + Decoder->getSubtargetInfo().hasFeature(RISCV::Feature64Bit) ? 64 : 32; + if (Imm != ExpectedValue) + return MCDisassembler::Fail; + + Inst.addOperand(MCOperand::createImm(Imm)); + return MCDisassembler::Success; +} + template static DecodeStatus decodeUImmNonZeroOperand(MCInst &Inst, uint32_t Imm, int64_t Address, @@ -699,6 +735,12 @@ static constexpr DecoderListEntry DecoderList32[]{ {DecoderTableXAndes32, XAndesGroup, "Andes extensions"}, {DecoderTableXSMT32, XSMTGroup, "SpacemiT extensions"}, // Standard Extensions + {DecoderTableRVY32Only32, + {RISCV::FeatureStdExtY, RISCV::Feature32Bit}, + "RVY32-only standard 32-bit instructions"}, + {DecoderTableRVYOnly32, + {RISCV::FeatureStdExtY}, + "RVY-only standard 32-bit instructions"}, {DecoderTable32, {}, "standard 32-bit instructions"}, {DecoderTableRV32Only32, {}, "RV32-only standard 32-bit instructions"}, {DecoderTableZfinx32, {}, "Zfinx (Float in Integer)"}, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h index 70b7c430c410e..16ec7e48f0e76 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -320,6 +320,7 @@ enum OperandType : unsigned { OPERAND_UIMM7, OPERAND_UIMM7_LSB00, OPERAND_UIMM7_LSB000, + OPERAND_UIMM7_SRLIY, OPERAND_UIMM8_LSB00, OPERAND_UIMM8, OPERAND_UIMM8_LSB000, diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp index 6d587e6f167fc..f8c6d34a965af 100644 --- a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -105,6 +105,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter { SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + unsigned getYBNDSWImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + unsigned getVMaskReg(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; @@ -718,6 +722,31 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, return 0; } +unsigned +RISCVMCCodeEmitter::getYBNDSWImmOpValue(const MCInst &MI, unsigned OpNo, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + unsigned Imm = getImmOpValue(MI, OpNo, Fixups, STI); + // The 10-bit immediate is encoded as `((imm[7:0] + 257) << imm[9:8]) - 256`. + if (Imm <= 256) { + assert(Imm > 0); // 1, 2, ..., 255, 256 + return Imm - 1; + } + if (Imm <= 768) { + assert(Imm % 2 == 0); // 258, 260, ..., 766, 768 + return ((Imm - 258) >> 1) | (1 << 8); + } + if (Imm <= 1792) { + assert(Imm % 4 == 0); // 772, 776, ..., 1788, 1792 + return ((Imm - 772) >> 2) | (2 << 8); + } + if (Imm <= 3840) { + assert(Imm % 8 == 0); // 1800, 1808, ..., 3832, 3840 + return ((Imm - 1800) >> 3) | (3 << 8); + } + llvm_unreachable("Invalid immediate for YBNDSWI"); +} + unsigned RISCVMCCodeEmitter::getVMaskReg(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index a02de31d1cc4d..52b46cf97eb28 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -60,10 +60,9 @@ class RISCVExtensionBitmask groupID, int bitPos> { // Version of RISCVExtension to be used for Experimental extensions. This // sets the Experimental flag and prepends experimental- to the -mattr name. -class RISCVExperimentalExtension implies = [], - string fieldname = !subst("Feature", "Has", NAME), - string value = "true"> +class RISCVExperimentalExtension< + int major, int minor, string desc, list implies = [], + string fieldname = !subst("Feature", "Has", NAME), string value = "true"> : RISCVExtension { let Experimental = true; } @@ -1126,6 +1125,28 @@ def HasStdExtZbbOrZbkbOrP "'Zbkb' (Bitmanip instructions for Cryptography) or " "'Base P' (Packed-SIMD)">; +// "Y" extension (CHERI support) + +def FeatureStdExtY : RISCVExperimentalExtension<0, 96, "'Base Y' (CHERI)">; +def HasStdExtY + : Predicate<"Subtarget->hasStdExtY()">, + AssemblerPredicate<(all_of FeatureStdExtY), "'Base Y' (CHERI)">; + +// When enabled all memory operations (e.g. loads/stores) uses capability +// registers as the base operand instead of the address sub-register. +// Currently, capability mode needs to be chosen at assembly time, but follow-up +// commits will add support for "hybrid" mode that adds instructions to +// dynamically switch between capability mode and address mode (the latter being +// fully backwards compatible with non-Y code). +def FeatureCapMode : SubtargetFeature<"cap-mode", "IsCapMode", "true", + "Capability pointer mode">; +def IsCapMode + : Predicate<"Subtarget->isCapMode()">, + AssemblerPredicate<(all_of FeatureCapMode), "Capability Pointer Mode">; +def NotCapMode : Predicate<"!Subtarget->isCapMode()">, + AssemblerPredicate<(all_of (not FeatureCapMode)), + "Not Capability Pointer Mode">; + //===----------------------------------------------------------------------===// // Vendor extensions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormatsY.td b/llvm/lib/Target/RISCV/RISCVInstrFormatsY.td new file mode 100644 index 0000000000000..cc32d124f515d --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrFormatsY.td @@ -0,0 +1,77 @@ +//===-- RISCVInstrFormatsY.td --------------------------------*- tablegen -*-=// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file describes the RISC-V Y extension instruction formats. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Operand definitions. +//===----------------------------------------------------------------------===// + +def YBNDSWImmOperand : AsmOperandClass { + let Name = "YBNDSWImm"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidYBNDSWImm"; +} + +def ybndsw_imm : Operand, ImmLeaf= 1 && Imm <= 256) || + (Imm >= 258 && Imm <= 768 && (Imm % 2) == 0) || + (Imm >= 772 && Imm <= 1792 && (Imm % 4) == 0) || + (Imm >= 1800 && Imm <= 3840 && (Imm % 8) == 0); +}]> { + let EncoderMethod = "getYBNDSWImmOpValue"; + let ParserMatchClass = YBNDSWImmOperand; + let DecoderMethod = "DecodeYBNDSWImm"; + let MIOperandInfo = (ops i32imm); +} + +def uimm7_srliy : RISCVUImmOp<7>, ImmLeafis64Bit() ? Imm == 64 : Imm == 32; +}]> { + let ParserMatchClass = UImmAsmOperand<7, "Srliy">; + let DecoderMethod = "decodeUImm7SrliyOperand"; + let MCOperandPredicate = [{ + int64_t Imm; + if (!MCOp.evaluateAsConstantImm(Imm)) + return false; + return Subtarget->is64Bit() ? Imm == 64 : Imm == 32; + }]; + let OperandType = "OPERAND_UIMM7_SRLIY"; +} + +//===----------------------------------------------------------------------===// +// Instruction Formats +//===----------------------------------------------------------------------===// + +// Like an RVInstR, except rs2 is now an additional function code. +class RVYInstSrcDst funct7, bits<5> funct5, bits<3> funct3, + RISCVOpcode opcode, dag outs, dag ins, string opcodestr, + string argstr> + : RVInst { + bits<5> rs1; + bits<5> rd; + + let Inst{31 -25} = funct7; + let Inst{24 -20} = funct5; + let Inst{19 -15} = rs1; + let Inst{14 -12} = funct3; + let Inst{11 -7} = rd; + let Inst{6 -0} = opcode.Value; +} + +class RVYInstSetBoundsImmFmt + : RVInstIBase<0b011, OPC_OP_IMM_32, outs, ins, opcodestr, argstr> { + bits<5> rd; + bits<5> rs1; + bits<10> imm; + + let Inst{31 -30} = 0b00; + let Inst{29 -20} = imm; +} diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index 9855c47a63392..6ad9049f4614a 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -227,6 +227,7 @@ def GPRMemZeroOffset : MemOperand { } def GPRMem : MemOperand; +def YGPRMem : MemOperand; def SPMem : MemOperand; @@ -643,9 +644,20 @@ class BranchCC_rri funct3, string opcodestr> } let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in { -class Load_ri funct3, string opcodestr, DAGOperand rty = GPR> - : RVInstI; +multiclass Load_ri funct3, string opcodestr, DAGOperand rty = GPR, + RISCVOpcode opcode=OPC_LOAD, list ExtraPreds=[], + string DecoderNS=""> { + let DecoderNamespace = DecoderNS, + Predicates = !listconcat(ExtraPreds, [NotCapMode]) in + def "" : RVInstI; + let DecoderNamespace = !if(!eq(DecoderNS, ""), "RVYOnly", !subst("RV", "RVY", DecoderNS)), + Predicates = !listconcat(ExtraPreds, [HasStdExtY, IsCapMode]) in + def _Y : RVInstI; +} class HLoad_r funct7, bits<5> funct5, string opcodestr> : RVInstR funct7, bits<5> funct5, string opcodestr> // reflecting the order these fields are specified in the instruction // encoding. let hasSideEffects = 0, mayLoad = 0, mayStore = 1 in { -class Store_rri funct3, string opcodestr, DAGOperand rty = GPR> - : RVInstS funct3, string opcodestr, DAGOperand rty = GPR, + list ExtraPreds = [], string DecoderNS = ""> { + let DecoderNamespace = DecoderNS, + Predicates = !listconcat(ExtraPreds, [NotCapMode]) in + def "" : RVInstS; + let DecoderNamespace = !if(!eq(DecoderNS, ""), "RVYOnly", !subst("RV", "RVY", DecoderNS)), + Predicates = !listconcat(ExtraPreds, [HasStdExtY, IsCapMode]) in + def _Y : RVInstS; +} class HStore_rr funct7, string opcodestr> : RVInstR; def BGEU : BranchCC_rri<0b111, "bgeu">; let IsSignExtendingOpW = 1 in { -def LB : Load_ri<0b000, "lb">, Sched<[WriteLDB, ReadMemBase]>; -def LH : Load_ri<0b001, "lh">, Sched<[WriteLDH, ReadMemBase]>; -def LW : Load_ri<0b010, "lw">, Sched<[WriteLDW, ReadMemBase]>; -def LBU : Load_ri<0b100, "lbu">, Sched<[WriteLDB, ReadMemBase]>; -def LHU : Load_ri<0b101, "lhu">, Sched<[WriteLDH, ReadMemBase]>; +defm LB : Load_ri<0b000, "lb">, Sched<[WriteLDB, ReadMemBase]>; +defm LH : Load_ri<0b001, "lh">, Sched<[WriteLDH, ReadMemBase]>; +defm LW : Load_ri<0b010, "lw">, Sched<[WriteLDW, ReadMemBase]>; +defm LBU : Load_ri<0b100, "lbu">, Sched<[WriteLDB, ReadMemBase]>; +defm LHU : Load_ri<0b101, "lhu">, Sched<[WriteLDH, ReadMemBase]>; } -def SB : Store_rri<0b000, "sb">, Sched<[WriteSTB, ReadStoreData, ReadMemBase]>; -def SH : Store_rri<0b001, "sh">, Sched<[WriteSTH, ReadStoreData, ReadMemBase]>; -def SW : Store_rri<0b010, "sw">, Sched<[WriteSTW, ReadStoreData, ReadMemBase]>; +defm SB : Store_rri<0b000, "sb">, Sched<[WriteSTB, ReadStoreData, ReadMemBase]>; +defm SH : Store_rri<0b001, "sh">, Sched<[WriteSTH, ReadStoreData, ReadMemBase]>; +defm SW : Store_rri<0b010, "sw">, Sched<[WriteSTW, ReadStoreData, ReadMemBase]>; // ADDI isn't always rematerializable, but isReMaterializable will be used as // a hint which is verified in isReMaterializableImpl. @@ -888,11 +909,14 @@ def CSRRCI : CSR_ii<0b111, "csrrci">; /// RV64I instructions -let Predicates = [IsRV64] in { -def LWU : Load_ri<0b110, "lwu">, Sched<[WriteLDW, ReadMemBase]>; -def LD : Load_ri<0b011, "ld">, Sched<[WriteLDD, ReadMemBase]>; -def SD : Store_rri<0b011, "sd">, Sched<[WriteSTD, ReadStoreData, ReadMemBase]>; +defm LWU : Load_ri<0b110, "lwu", ExtraPreds=[IsRV64]>, + Sched<[WriteLDW, ReadMemBase]>; +defm LD : Load_ri<0b011, "ld", ExtraPreds=[IsRV64]>, + Sched<[WriteLDD, ReadMemBase]>; +defm SD : Store_rri<0b011, "sd", ExtraPreds=[IsRV64]>, + Sched<[WriteSTD, ReadStoreData, ReadMemBase]>; +let Predicates = [IsRV64] in { let IsSignExtendingOpW = 1 in { let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in def ADDIW : RVInstI<0b000, OPC_OP_IMM_32, (outs GPR:$rd), @@ -2363,6 +2387,9 @@ include "RISCVInstrInfoZicfiss.td" // Short Forward Branch include "RISCVInstrInfoSFB.td" +// CHERI +include "RISCVInstrInfoY.td" + //===----------------------------------------------------------------------===// // Vendor extensions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td index fde030ecc3b89..8e5eafba2e7f6 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoF.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoF.td @@ -337,15 +337,16 @@ def FLW : FPLoad_r<0b010, "flw", FPR32, WriteFLD32>; def FSW : FPStore_r<0b010, "fsw", FPR32, WriteFST32>; } // Predicates = [HasStdExtF] -let Predicates = [HasStdExtZfinx], isCodeGenOnly = 1 in { -def LW_INX : Load_ri<0b010, "lw", GPRF32>, Sched<[WriteLDW, ReadMemBase]>; -def SW_INX : Store_rri<0b010, "sw", GPRF32>, +let isCodeGenOnly = 1 in { +defm LW_INX : Load_ri<0b010, "lw", GPRF32, ExtraPreds=[HasStdExtZfinx]>, + Sched<[WriteLDW, ReadMemBase]>; +defm SW_INX : Store_rri<0b010, "sw", GPRF32, ExtraPreds=[HasStdExtZfinx]>, Sched<[WriteSTW, ReadStoreData, ReadMemBase]>; // ADDI with GPRF32 register class to use for copy. This should not be used as // general ADDI, so the immediate should always be zero. let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveReg = 1, - hasSideEffects = 0, mayLoad = 0, mayStore = 0 in + hasSideEffects = 0, mayLoad = 0, mayStore = 0, Predicates=[HasStdExtZfinx] in def PseudoMV_FPR32INX : Pseudo<(outs GPRF32:$rd), (ins GPRF32:$rs), []>, Sched<[WriteIALU, ReadIALU]>; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoY.td b/llvm/lib/Target/RISCV/RISCVInstrInfoY.td new file mode 100644 index 0000000000000..f746075f0dfba --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoY.td @@ -0,0 +1,115 @@ +//===-- RISCVInstrInfoY.td - RISCV instructions -------------*- tblgen-*---===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Instruction Formats +//===----------------------------------------------------------------------===// + +include "RISCVInstrFormatsY.td" + +//===----------------------------------------------------------------------===// +// Instruction Class Templates +//===----------------------------------------------------------------------===// + +let hasSideEffects = 0, mayLoad = 0, mayStore = 0 in { + class RVY_r funct5, string opcodestr, DAGOperand rdOp = GPR, + DAGOperand rs1Op = YGPR> + : RVYInstSrcDst<0x8, funct5, 0x0, OPC_OP, (outs rdOp:$rd), + (ins rs1Op:$rs1), opcodestr, "$rd, $rs1">; + class RVY_rr funct7, bits<3> funct3, string opcodestr, + DAGOperand rdOp = YGPR, DAGOperand rs1Op = YGPR, + DAGOperand rs2Op = GPR> + : RVInstR; + class RVY_ri funct3, RISCVOpcode opcode, string opcodestr, + DAGOperand rdOp = YGPR, DAGOperand rs1Op = YGPR> + : RVInstI; + class RVY_setboundsimm + : RVYInstSetBoundsImmFmt<(outs YGPR:$rd), + (ins YGPR:$rs1, ybndsw_imm:$imm), opcodestr, + "$rd, $rs1, $imm">; +} // hasSideEffects = 0, mayLoad = 0, mayStore = 0 + +let Predicates = [HasStdExtY] in { + // + // Instructions to Update The Capability Pointer + // + let isReMaterializable = true, isAsCheapAsAMove = true in { + def ADDY : RVY_rr<0x6, 0x0, "addy", YGPR, YGPR, GPRNoX0>; + def ADDIY : RVY_ri<0x2, OPC_OP_IMM_32, "addiy">; + } + def : InstAlias<"addy $rd, $rs1, $imm12", + (ADDIY YGPR:$rd, YGPR:$rs1, simm12_lo:$imm12), 0>; + def YADDRW : RVY_rr<0x6, 0x1, "yaddrw", YGPR, YGPR, GPR>; + + // + // Instructions to Manipulate Capabilities + // + // TODO: should we support acperm/candperm aliases with inverted mask? + def YPERMC : RVY_rr<0x6, 0x2, "ypermc", YGPR, YGPR, GPR>; + let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isReMaterializable = true, + isAsCheapAsAMove = true, isMoveReg = true in { + def YMV : RVYInstSrcDst<0x6, 0x0, 0x0, OPC_OP, (outs YGPR:$rd), + (ins YGPR:$rs1), "ymv", "$rd, $rs1">; + } + def PACKY : RVY_rr<0x6, 0x3, "packy", YGPR, YGPR, GPR>; + def : InstAlias<"yhiw $rd, $rs1, $rs2", (PACKY YGPR:$rd, YGPR:$rs1, GPR:$rs2), + 0>; + def YBNDSW : RVY_rr<0x7, 0x0, "ybndsw", YGPR, YGPR, GPR>; + def YBNDSWI : RVY_setboundsimm<"ybndswi">; + def : InstAlias<"ybndsw $rd, $rs1, $imm", + (YBNDSWI YGPR:$rd, YGPR:$rs1, ybndsw_imm:$imm), 0>; + def YBNDSRW : RVY_rr<0x7, 0x1, "ybndsrw", YGPR, YGPR, GPR>; + def YBLD : RVY_rr<0x6, 0x5, "ybld", YGPR, YGPR, YGPR>; + def YSUNSEAL : RVY_rr<0x7, 0x2, "ysunseal", YGPR, YGPR, YGPR>; + // + // Instructions to Decode Capability Bounds + // + def YBASER : RVY_r<0x5, "ybaser", GPR, YGPR>; + def YLENR : RVY_r<0x6, "ylenr", GPR, YGPR>; + + // + // Instructions to Extract Capability Fields + // + def YTAGR : RVY_r<0x0, "ytagr", GPR, YGPR>; + def YPERMR : RVY_r<0x1, "ypermr", GPR, YGPR>; + def YTYPER : RVY_r<0x2, "ytyper", GPR, YGPR>; + // The SRLIY instruction uses the encoding of SRLI with shamt==XLEN, other + // values are not supported. YHIR is a pseudo that expands to the correct + // shift value depending on RV32/RV64. + let DecoderNamespace = "RVYOnly", hasSideEffects = 0, mayLoad = 0, + mayStore = 0 in { + def SRLIY : RVInstIBase<0b101, OPC_OP_IMM, (outs GPR:$rd), + (ins YGPR:$rs1, uimm7_srliy:$shamt), "srliy", + "$rd, $rs1, $shamt"> { + bits<7> shamt; + let Inst{31 -27} = 0b00000; + let Inst{26 -20} = shamt; + } + } + let Predicates = [HasStdExtY, IsRV64] in { + def : InstAlias<"yhir $rd, $rs1", (SRLIY GPR:$rd, YGPR:$rs1, 64)>; + } + let Predicates = [HasStdExtY, IsRV32] in { + def : InstAlias<"yhir $rd, $rs1", (SRLIY GPR:$rd, YGPR:$rs1, 32)>; + } + // + // Miscellaneous Instructions to Handle Capability Data + // + def SYEQ : RVY_rr<0x6, 0x4, "syeq", GPR, YGPR, YGPR>; + def YLT : RVY_rr<0x6, 0x6, "ylt", GPR, YGPR, YGPR>; + def YAMASK : RVY_r<0x7, "yamask", GPR, GPR>; +} // Predicates = [HasStdExtY] + +// Instructions to Load and Store Capability Data +defm LY : Load_ri<0b100, "ly", opcode=OPC_MISC_MEM, ExtraPreds=[HasStdExtY]>, + Sched<[/* TODO: WriteLDY, */ ReadMemBase]>; +defm SY : Store_rri<0b100, "sy", ExtraPreds=[HasStdExtY]>, + Sched<[/* TODO: WriteSTY, */ ReadStoreData, ReadMemBase]>; diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td index 014da990a0146..c6b5cb3a235bc 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td @@ -98,15 +98,17 @@ def FLH : FPLoad_r<0b001, "flh", FPR16, WriteFLD16>; def FSH : FPStore_r<0b001, "fsh", FPR16, WriteFST16>; } // Predicates = [HasHalfFPLoadStoreMove] -let Predicates = [HasStdExtZhinxmin], isCodeGenOnly = 1 in { -def LH_INX : Load_ri<0b001, "lh", GPRF16>, Sched<[WriteLDH, ReadMemBase]>; -def SH_INX : Store_rri<0b001, "sh", GPRF16>, - Sched<[WriteSTH, ReadStoreData, ReadMemBase]>; +let isCodeGenOnly = 1 in { +defm LH_INX : Load_ri<0b001, "lh", GPRF16, ExtraPreds=[HasStdExtZfinx]>, + Sched<[WriteLDH, ReadMemBase]>; +defm SH_INX : Store_rri<0b001, "sh", GPRF16, ExtraPreds=[HasStdExtZfinx]>, + Sched<[WriteSTH, ReadStoreData, ReadMemBase]>; // ADDI with GPRF16 register class to use for copy. This should not be used as // general ADDI, so the immediate should always be zero. let isReMaterializable = 1, isAsCheapAsAMove = 1, isMoveReg = 1, - hasSideEffects = 0, mayLoad = 0, mayStore = 0 in + hasSideEffects = 0, mayLoad = 0, mayStore = 0, + Predicates = [HasStdExtZhinxmin] in def PseudoMV_FPR16INX : Pseudo<(outs GPRF16:$rd), (ins GPRF16:$rs), []>, Sched<[WriteIALU, ReadIALU]>; } diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td index a3203f288b545..fc5c0a5d5e754 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td @@ -33,11 +33,12 @@ def riscv_st_rv32 : RVSDNode<"SD_RV32", SDT_RISCV_SD_RV32, // Instructions //===----------------------------------------------------------------------===// -let Predicates = [HasStdExtZilsd, IsRV32], DecoderNamespace = "RV32Only" in { -def LD_RV32 : Load_ri<0b011, "ld", GPRPairRV32>, Sched<[WriteLDD, ReadMemBase]>; -def SD_RV32 : Store_rri<0b011, "sd", GPRPairRV32>, - Sched<[WriteSTD, ReadStoreData, ReadMemBase]>; -} // Predicates = [HasStdExtZilsd, IsRV32], DecoderNamespace = "RV32Only" +defm LD_RV32 : Load_ri<0b011, "ld", GPRPairRV32, DecoderNS="RV32Only", + ExtraPreds=[HasStdExtZilsd, IsRV32]>, + Sched<[WriteLDD, ReadMemBase]>; +defm SD_RV32 : Store_rri<0b011, "sd", GPRPairRV32, DecoderNS="RV32Only", + ExtraPreds=[HasStdExtZilsd, IsRV32]>, + Sched<[WriteSTD, ReadStoreData, ReadMemBase]>; //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions diff --git a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td index 82e768d7c1d16..87465e5466d7e 100644 --- a/llvm/lib/Target/RISCV/RISCVRegisterInfo.td +++ b/llvm/lib/Target/RISCV/RISCVRegisterInfo.td @@ -52,6 +52,17 @@ class RISCVReg128 let SubRegIndices = [sub_64]; } +// A subreg index for the address part of capability registers (this is just the +// default XLEN-wide X register). +def sub_cap_addr : SubRegIndex<32> { + let SubRegRanges = + SubRegRangeByHwMode<[RV32, RV64], [SubRegRange<32>, SubRegRange<64>]>; +} +class RISCVCapReg alt = []> + : RISCVRegWithSubRegs { + let SubRegIndices = [sub_cap_addr]; +} + let FallbackRegAltNameIndex = NoRegAltName in def ABIRegAltName : RegAltNameIndex; @@ -211,6 +222,46 @@ let RegAltNameIndices = [ABIRegAltName] in { def X31 : RISCVRegWithSubRegs<31,"x31", [X31_W], ["t6"]>, DwarfRegNum<[31]>; } } + // CHERI capability registers (Y extension) + let SubRegIndices = [sub_cap_addr] in { + let isConstant = true in def X0_Y : RISCVCapReg, + DwarfRegAlias; + let CostPerUse = [0, 1] in { + def X1_Y : RISCVCapReg, DwarfRegAlias; + def X2_Y : RISCVCapReg, DwarfRegAlias; + def X3_Y : RISCVCapReg, DwarfRegAlias; + def X4_Y : RISCVCapReg, DwarfRegAlias; + def X5_Y : RISCVCapReg, DwarfRegAlias; + def X6_Y : RISCVCapReg, DwarfRegAlias; + def X7_Y : RISCVCapReg, DwarfRegAlias; + } + def X8_Y : RISCVCapReg, DwarfRegAlias; + def X9_Y : RISCVCapReg, DwarfRegAlias; + def X10_Y : RISCVCapReg, DwarfRegAlias; + def X11_Y : RISCVCapReg, DwarfRegAlias; + def X12_Y : RISCVCapReg, DwarfRegAlias; + def X13_Y : RISCVCapReg, DwarfRegAlias; + def X14_Y : RISCVCapReg, DwarfRegAlias; + def X15_Y : RISCVCapReg, DwarfRegAlias; + let CostPerUse = [0, 1] in { + def X16_Y : RISCVCapReg, DwarfRegAlias; + def X17_Y : RISCVCapReg, DwarfRegAlias; + def X18_Y : RISCVCapReg, DwarfRegAlias; + def X19_Y : RISCVCapReg, DwarfRegAlias; + def X20_Y : RISCVCapReg, DwarfRegAlias; + def X21_Y : RISCVCapReg, DwarfRegAlias; + def X22_Y : RISCVCapReg, DwarfRegAlias; + def X23_Y : RISCVCapReg, DwarfRegAlias; + def X24_Y : RISCVCapReg, DwarfRegAlias; + def X25_Y : RISCVCapReg, DwarfRegAlias; + def X26_Y : RISCVCapReg, DwarfRegAlias; + def X27_Y : RISCVCapReg, DwarfRegAlias; + def X28_Y : RISCVCapReg, DwarfRegAlias; + def X29_Y : RISCVCapReg, DwarfRegAlias; + def X30_Y : RISCVCapReg, DwarfRegAlias; + def X31_Y : RISCVCapReg, DwarfRegAlias; + } + } } def XLenVT : ValueTypeByHwMode<[RV32, RV64], @@ -225,6 +276,9 @@ def XLenPairFVT : ValueTypeByHwMode<[RV32], def XLenRI : RegInfoByHwMode< [RV32, RV64], [RegInfo<32,32,32>, RegInfo<64,64,64>]>; +def YLenVT : ValueTypeByHwMode<[RV32, RV64], [c64, c128]>; +def YLenRI : RegInfoByHwMode<[RV32, RV64], [RegInfo<64, 64, 64>, + RegInfo<128, 128, 128>]>; class RISCVRegisterClass regTypes, int align, dag regList> : RegisterClass<"RISCV", regTypes, align, regList> { @@ -251,6 +305,14 @@ def GPR : GPRRegisterClass<(add (sequence "X%u", 10, 17), (sequence "X%u", 18, 27), (sequence "X%u", 0, 4))>; +def YGPR + : RegisterClass<"RISCV", [YLenVT], 64, + (add (sequence "X%u_Y", 10, 17), (sequence "X%u_Y", 5, 7), + (sequence "X%u_Y", 28, 31), (sequence "X%u_Y", 8, 9), + (sequence "X%u_Y", 18, 27), (sequence "X%u_Y", 0, 4))> { + let RegInfos = YLenRI; +} + def GPRX0 : GPRRegisterClass<(add X0)>; def GPRX1 : GPRRegisterClass<(add X1)> { diff --git a/llvm/lib/Target/RISCV/RISCVSchedule.td b/llvm/lib/Target/RISCV/RISCVSchedule.td index 9ab9636a79cbe..7df0ed0a3258f 100644 --- a/llvm/lib/Target/RISCV/RISCVSchedule.td +++ b/llvm/lib/Target/RISCV/RISCVSchedule.td @@ -27,11 +27,13 @@ def WriteLDB : SchedWrite; // Load byte def WriteLDH : SchedWrite; // Load half-word def WriteLDW : SchedWrite; // Load word def WriteLDD : SchedWrite; // Load double-word +// TODO: def WriteLDY : SchedWrite; // Load capability def WriteCSR : SchedWrite; // CSR instructions def WriteSTB : SchedWrite; // Store byte def WriteSTH : SchedWrite; // Store half-word def WriteSTW : SchedWrite; // Store word def WriteSTD : SchedWrite; // Store double-word +// TODO: def WriteSTY : SchedWrite; // Store capability def WriteAtomicB : SchedWrite; //Atomic memory operation byte size def WriteAtomicH : SchedWrite; //Atomic memory operation halfword size def WriteAtomicW : SchedWrite; //Atomic memory operation word size diff --git a/llvm/test/MC/RISCV/invalid-attribute.s b/llvm/test/MC/RISCV/invalid-attribute.s index c640fccd15ae5..bdc10790157d7 100644 --- a/llvm/test/MC/RISCV/invalid-attribute.s +++ b/llvm/test/MC/RISCV/invalid-attribute.s @@ -13,8 +13,8 @@ .attribute arch, "foo" # CHECK: [[@LINE-1]]:18: error: invalid arch name 'foo', string must begin with rv32{i,e,g}, rv64{i,e,g}, or a supported profile name{{$}} -.attribute arch, "rv32i2p1_y2p0" -# CHECK: [[@LINE-1]]:18: error: invalid arch name 'rv32i2p1_y2p0', invalid standard user-level extension 'y' +.attribute arch, "rv32i2p1_o2p0" +# CHECK: [[@LINE-1]]:18: error: invalid arch name 'rv32i2p1_o2p0', invalid standard user-level extension 'o' .attribute stack_align, "16" # CHECK: [[@LINE-1]]:25: error: expected numeric constant diff --git a/llvm/test/MC/RISCV/rvy/rv32y-invalid-mode-independent.s b/llvm/test/MC/RISCV/rvy/rv32y-invalid-mode-independent.s new file mode 100644 index 0000000000000..608becf94983e --- /dev/null +++ b/llvm/test/MC/RISCV/rvy/rv32y-invalid-mode-independent.s @@ -0,0 +1,42 @@ +# RUN: not llvm-mc --triple riscv32 --mattr=+experimental-y <%s 2>&1 \ +# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 '--implicit-check-not=error:' +# RUN: not llvm-mc --triple riscv64 --mattr=+experimental-y <%s 2>&1 \ +# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-64 '--implicit-check-not=error:' + +addy a0, a1, zero +# CHECK: :[[#@LINE-1]]:14: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047] +addy a0, a1, x0 +# CHECK: :[[#@LINE-1]]:14: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047] +srliy a0, a1, 65 +# CHECK-32: :[[#@LINE-1]]:15: error: immediate must be an integer equal to XLEN (32) +# CHECK-64: :[[#@LINE-2]]:15: error: immediate must be an integer equal to XLEN (64) +srliy a0, a1, 64 +# CHECK-32: :[[#@LINE-1]]:15: error: immediate must be an integer equal to XLEN (32) +srliy a0, a1, 32 +# CHECK-64: :[[#@LINE-1]]:15: error: immediate must be an integer equal to XLEN (64) +ybndswi a0, a0, 0 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 257 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 259 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 767 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 769 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 773 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 774 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 1790 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 1794 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 1804 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 3846 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 3848 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] +ybndswi a0, a0, 4096 +# CHECK: :[[#@LINE-1]]:17: error: immediate must be an integer in the range [1, 256], a multiple of 2 in the range [258, 768], a multiple of 4 in the range [772, 1792], or a multiple of 8 in the range [1800, 3840] diff --git a/llvm/test/MC/RISCV/rvy/rv32y-valid-mode-independent.s b/llvm/test/MC/RISCV/rvy/rv32y-valid-mode-independent.s new file mode 100644 index 0000000000000..0353c3031ce62 --- /dev/null +++ b/llvm/test/MC/RISCV/rvy/rv32y-valid-mode-independent.s @@ -0,0 +1,132 @@ +# RUN: llvm-mc %s --triple=riscv32 -mattr=+experimental-y --riscv-no-aliases --show-encoding \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-32,CHECK-ASM-AND-OBJ-NEXT '-D#XLEN=32' %s +# RUN: llvm-mc --filetype=obj --triple=riscv32 --mattr=+experimental-y < %s \ +# RUN: | llvm-objdump --mattr=+experimental-y -M no-aliases -d --no-print-imm-hex - \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ-NEXT '-D#XLEN=32' %s +# +# RUN: llvm-mc %s --triple=riscv64 --mattr=+experimental-y --riscv-no-aliases \ +# RUN: --show-encoding --defsym=RV64=1 \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-64,CHECK-ASM-AND-OBJ-NEXT '-D#XLEN=64' %s +# RUN: llvm-mc --filetype=obj --triple=riscv64 --mattr=+experimental-y \ +# RUN: --riscv-no-aliases --show-encoding --defsym=RV64=1 < %s \ +# RUN: | llvm-objdump --mattr=+experimental-y -M no-aliases -d --no-print-imm-hex - \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ-NEXT '-D#XLEN=64' %s + +# CHECK-ASM-AND-OBJ: addy a0, a0, a1 +# CHECK-ASM: encoding: [0x33,0x05,0xb5,0x0c] +addy a0, a0, a1 +# CHECK-ASM-AND-OBJ-NEXT: addiy a0, a0, 12 +# CHECK-ASM-SAME: encoding: [0x1b,0x25,0xc5,0x00] +addiy a0, a0, 12 +# CHECK-ASM-AND-OBJ-NEXT: addiy a0, a0, 12 +# CHECK-ASM-SAME: encoding: [0x1b,0x25,0xc5,0x00] +addy a0, a0, 12 +# CHECK-ASM-AND-OBJ-NEXT: yaddrw a0, a0, a1 +# CHECK-ASM-SAME: encoding: [0x33,0x15,0xb5,0x0c] +yaddrw a0, a0, a1 +# +# +# CHECK-ASM-AND-OBJ-NEXT: ypermc a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x25,0xa5,0x0c] +ypermc a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ymv a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x05,0x0c] +ymv a0, a0 +## Note: mv expands to integer addi and not capability ymv: +# CHECK-ASM-AND-OBJ-NEXT: addi a0, a0, +# CHECK-ASM-SAME: encoding: [0x13,0x05,0x05,0x00] +mv a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: packy a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x35,0xa5,0x0c] +packy a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: packy a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x35,0xa5,0x0c] +yhiw a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ybndsw a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0xa5,0x0e] +ybndsw a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ybndsrw a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x15,0xa5,0x0e] +ybndsrw a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 12 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xb5,0x00] +ybndswi a0, a0, 12 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 12 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xb5,0x00] +ybndsw a0, a0, 12 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 12 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xb5,0x00] +ybndswi a0, a0, 12 +## Test all the min and max values for the ybndswi encoding +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 1 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0x05,0x00] +ybndswi a0, a0, 1 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 256 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xf5,0x0f] +ybndswi a0, a0, 256 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 258 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0x05,0x10] +ybndswi a0, a0, 258 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 768 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xf5,0x1f] +ybndswi a0, a0, 768 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 772 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0x05,0x20] +ybndswi a0, a0, 772 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 1792 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xf5,0x2f] +ybndswi a0, a0, 1792 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 1800 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0x05,0x30] +ybndswi a0, a0, 1800 +# CHECK-ASM-AND-OBJ-NEXT: ybndswi a0, a0, 3840 +# CHECK-ASM-SAME: encoding: [0x1b,0x35,0xf5,0x3f] +ybndswi a0, a0, 3840 +# CHECK-ASM-AND-OBJ-NEXT: ybld a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x55,0xa5,0x0c] +ybld a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ysunseal a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x25,0xa5,0x0e] +ysunseal a0, a0, a0 +# +# +# CHECK-ASM-AND-OBJ-NEXT: ybaser a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x55,0x10] +ybaser a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ylenr a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x65,0x10] +ylenr a0, a0 +# +# +# CHECK-ASM-AND-OBJ-NEXT: ytagr a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x05,0x10] +ytagr a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ypermr a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x15,0x10] +ypermr a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ytyper a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x25,0x10] +ytyper a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: srliy a0, a0, [[#XLEN]] +.ifdef RV64 +# CHECK-ASM-64-SAME: encoding: [0x13,0x55,0x05,0x04] +srliy a0, a0, 64 +.else +# CHECK-ASM-32-SAME: encoding: [0x13,0x55,0x05,0x02] +srliy a0, a0, 32 +.endif +# CHECK-ASM-AND-OBJ-NEXT: srliy a0, a0, [[#XLEN]] +# CHECK-ASM-32-SAME: encoding: [0x13,0x55,0x05,0x02] +# CHECK-ASM-64-SAME: encoding: [0x13,0x55,0x05,0x04] +yhir a0, a0 +# +# +# CHECK-ASM-AND-OBJ-NEXT: syeq a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x45,0xa5,0x0c] +syeq a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: ylt a0, a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x65,0xa5,0x0c] +ylt a0, a0, a0 +# CHECK-ASM-AND-OBJ-NEXT: yamask a0, a0 +# CHECK-ASM-SAME: encoding: [0x33,0x05,0x75,0x10] +yamask a0, a0 diff --git a/llvm/test/MC/RISCV/rvy/rvy-cap-mode-invalid.s b/llvm/test/MC/RISCV/rvy/rvy-cap-mode-invalid.s new file mode 100644 index 0000000000000..a5758b6203b2d --- /dev/null +++ b/llvm/test/MC/RISCV/rvy/rvy-cap-mode-invalid.s @@ -0,0 +1,15 @@ +# RUN: not llvm-mc --triple riscv32 --mattr=+experimental-y,+cap-mode <%s 2>&1 \ +# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-32 '--implicit-check-not=error:' +# RUN: not llvm-mc --triple riscv64 --mattr=+experimental-y,+cap-mode <%s 2>&1 \ +# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-64 '--implicit-check-not=error:' + +# TODO: support expanding these pseudos +lw a0, sym +ld a0, sym +# CHECK-32: [[#@LINE-1]]:1: error: instruction requires the following: 'Zilsd' (Load/Store pair instructions) +ly a0, sym +# CHECK: [[#@LINE-1]]:8: error: operand must be a symbol with %lo/%pcrel_lo/%tprel_lo specifier or an integer in the range [-2048, 2047] + +.data +sym: +.4byte 0 diff --git a/llvm/test/MC/RISCV/rvy/rvy-valid-load-store.s b/llvm/test/MC/RISCV/rvy/rvy-valid-load-store.s new file mode 100644 index 0000000000000..efad4b79a1fd1 --- /dev/null +++ b/llvm/test/MC/RISCV/rvy/rvy-valid-load-store.s @@ -0,0 +1,78 @@ +# RUN: llvm-mc %s --triple=riscv32 -mattr=+experimental-y --riscv-no-aliases --show-encoding --show-inst \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ -DSUFFIX= %s +# RUN: llvm-mc %s --triple=riscv32 -mattr=+experimental-y,+cap-mode --riscv-no-aliases --show-encoding --show-inst \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-AND-OBJ -DSUFFIX=_Y %s +# RUN: llvm-mc --filetype=obj --triple=riscv32 --mattr=+experimental-y < %s \ +# RUN: | llvm-objdump --mattr=+experimental-y -M no-aliases -d --no-print-imm-hex - \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ %s +# +# RUN: llvm-mc %s --triple=riscv64 --mattr=+experimental-y --riscv-no-aliases \ +# RUN: --show-encoding --show-inst --defsym=RV64=1 \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-64,CHECK-ASM-AND-OBJ,CHECK-ASM-AND-OBJ-64 -DSUFFIX= %s +# RUN: llvm-mc %s --triple=riscv64 --mattr=+experimental-y,+cap-mode --riscv-no-aliases \ +# RUN: --show-encoding --show-inst --defsym=RV64=1 \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM,CHECK-ASM-64,CHECK-ASM-AND-OBJ,CHECK-ASM-AND-OBJ-64 -DSUFFIX=_Y %s +# RUN: llvm-mc --filetype=obj --triple=riscv64 --mattr=+experimental-y \ +# RUN: --riscv-no-aliases --show-encoding --defsym=RV64=1 < %s \ +# RUN: | llvm-objdump --mattr=+experimental-y -M no-aliases -d --no-print-imm-hex - \ +# RUN: | FileCheck --check-prefixes=CHECK-ASM-AND-OBJ,CHECK-ASM-AND-OBJ-64 %s + +## Both capability normal RISC-V instruction use the same encoding, but we have +## to check that we select the correct MCInst: for +cap-mode we want a _Y suffix + +# CHECK-ASM-AND-OBJ: lb a0, 0(a1) +# CHECK-ASM-SAME: # encoding: [0x03,0x85,0x05,0x00] +# CHECK-ASM-NEXT: Opcode) " "<< \"\\n\");\n"; - + // If we have a mismatch because we're missing one of the features that are + // conflicting for diagnostics purposes, skip this mnemonic. + OS << " if (!HasRequiredFeatures &&\n"; + OS << " (RequiredFeatures & ConflictingFeatures & ~AvailableFeatures).any()) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \" Skipping mnemonic \"\n"; + OS << " << MII.getName(it->Opcode) << \" due to conflicting features\\n\");\n"; + OS << " continue;\n"; + OS << " }\n"; + OS << " HasNonConflictingMnemonic = true;\n"; + OS << " if (!FoundValidMnemonic) {\n"; + if (!ReportMultipleNearMisses) + OS << " FirstValidMnemonic = it;\n"; + OS << " FoundValidMnemonic = true;\n"; + OS << " }\n"; if (ReportMultipleNearMisses) { OS << " // Some state to record ways in which this instruction did not " "match.\n"; @@ -3942,7 +3960,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " // If we already had a match that only failed due to a\n"; OS << " // target predicate, that diagnostic is preferred.\n"; OS << " if (!HadMatchOtherThanPredicate &&\n"; - OS << " (it == MnemonicRange.first || ErrorInfo <= ActualIdx)) " + OS << " (it == FirstValidMnemonic || ErrorInfo <= ActualIdx)) " "{\n"; OS << " if (HasRequiredFeatures && (ErrorInfo != ActualIdx || Diag " "!= Match_InvalidOperand))\n"; @@ -3980,6 +3998,11 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " if (NewMissingFeatures[I])\n"; OS << " dbgs() << ' ' << I;\n"; OS << " dbgs() << \"\\n\");\n"; + OS << " if ((NewMissingFeatures & ConflictingFeatures).any()) {\n"; + if (!ReportMultipleNearMisses) + OS << " HadMatchOtherThanFeatures = false;\n"; + OS << " continue;\n"; + OS << " }\n"; if (ReportMultipleNearMisses) { OS << " FeaturesNearMiss = " "NearMissInfo::getMissedFeature(NewMissingFeatures);\n"; @@ -4190,6 +4213,11 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " return Match_Success;\n"; OS << " }\n\n"; + OS << " if (!HasNonConflictingMnemonic) {\n"; + OS << " DEBUG_WITH_TYPE(\"asm-matcher\", dbgs() << \"Could not find non-conflicting mnemonic\\n\");\n"; + OS << " return Match_MnemonicFail;\n"; + OS << " }\n"; + if (ReportMultipleNearMisses) { OS << " // No instruction variants matched exactly.\n"; OS << " return Match_NearMisses;\n";