From 776913759faad445dcf47e8ae7442ab1fdc65114 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Sun, 21 Sep 2025 19:11:09 -0700 Subject: [PATCH 1/6] [RISC-V][MC] Add a RegisterClass definition for Y extension (CHERI) This is the first commit in a series of changes to add initial MC-layer support for the upcoming Y extension for CHERI. Specification: https://riscv.github.io/riscv-cheri/ Co-authored-by: Jessica Clarke --- llvm/lib/Target/RISCV/RISCVRegisterInfo.td | 62 ++++++++++++++++++++++ 1 file changed, 62 insertions(+) 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)> { From 91fb498bb60f91055ca6f6a016bd1d966f00ef54 Mon Sep 17 00:00:00 2001 From: Petr Vesely Date: Wed, 13 Aug 2025 05:19:04 -0700 Subject: [PATCH 2/6] [RISC-V][MC] Introduce RVY extension feature This adds initial features for the base RVY extension, other extensions such as the hybrid mode will be added later. Co-authored-by: Jessica Clarke Co-authored-by: Alexander Richardson Co-authored-by: Petr Vesely --- llvm/lib/Target/RISCV/RISCVFeatures.td | 24 +++++++++++++++++++++++- llvm/test/MC/RISCV/invalid-attribute.s | 4 ++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVFeatures.td b/llvm/lib/Target/RISCV/RISCVFeatures.td index a02de31d1cc4d..02f7d8bd6990e 100644 --- a/llvm/lib/Target/RISCV/RISCVFeatures.td +++ b/llvm/lib/Target/RISCV/RISCVFeatures.td @@ -61,7 +61,7 @@ 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 = [], + list implies = [], string fieldname = !subst("Feature", "Has", NAME), string value = "true"> : RISCVExtension { @@ -1126,6 +1126,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/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 From 3c009d2a204f28b093eebafca4a6e2ea07661790 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 6 Oct 2025 21:43:58 -0700 Subject: [PATCH 3/6] [RISC-V][MC] Introduce initial support for Y extension (CHERI) This adds MC-level support for most of the base Y extension instructions, restricted to the execution-mode-independent subset. The Y extension (CHERI for RISC-V) also introduces an execution mode that determines whether certain register operands use the full extended register or only the address subset (the current XLEN registers). The instructions that depend on execution mode (loads/stores/jumps + AUIPC) will be added in the next commit in this stack of changes. Co-authored-by: Jessica Clarke Co-authored-by: Alexander Richardson Co-authored-by: Petr Vesely --- .../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 56 +++++++- .../RISCV/Disassembler/RISCVDisassembler.cpp | 39 ++++++ .../Target/RISCV/MCTargetDesc/RISCVBaseInfo.h | 1 + .../RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp | 29 ++++ llvm/lib/Target/RISCV/RISCVFeatures.td | 9 +- llvm/lib/Target/RISCV/RISCVInstrFormatsY.td | 77 ++++++++++ llvm/lib/Target/RISCV/RISCVInstrInfo.td | 3 + llvm/lib/Target/RISCV/RISCVInstrInfoY.td | 109 +++++++++++++++ .../rvy/rv32y-invalid-mode-independent.s | 42 ++++++ .../RISCV/rvy/rv32y-valid-mode-independent.s | 132 ++++++++++++++++++ 10 files changed, 491 insertions(+), 6 deletions(-) create mode 100644 llvm/lib/Target/RISCV/RISCVInstrFormatsY.td create mode 100644 llvm/lib/Target/RISCV/RISCVInstrInfoY.td create mode 100644 llvm/test/MC/RISCV/rvy/rv32y-invalid-mode-independent.s create mode 100644 llvm/test/MC/RISCV/rvy/rv32y-valid-mode-independent.s diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index 21dbb7cbc9844..365db032abdbc 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -491,6 +491,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 +772,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 +809,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 +1339,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 +1371,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 +1750,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)) { diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index b8ec0bbfcd3bb..548c4ea1c4872 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,9 @@ static constexpr DecoderListEntry DecoderList32[]{ {DecoderTableXAndes32, XAndesGroup, "Andes extensions"}, {DecoderTableXSMT32, XSMTGroup, "SpacemiT extensions"}, // Standard Extensions + {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 02f7d8bd6990e..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; } @@ -1145,7 +1144,7 @@ def IsCapMode : Predicate<"Subtarget->isCapMode()">, AssemblerPredicate<(all_of FeatureCapMode), "Capability Pointer Mode">; def NotCapMode : Predicate<"!Subtarget->isCapMode()">, - AssemblerPredicate<(all_of(not FeatureCapMode)), + AssemblerPredicate<(all_of (not FeatureCapMode)), "Not Capability Pointer Mode">; //===----------------------------------------------------------------------===// 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..da32c9c0d738f 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td @@ -2363,6 +2363,9 @@ include "RISCVInstrInfoZicfiss.td" // Short Forward Branch include "RISCVInstrInfoSFB.td" +// CHERI +include "RISCVInstrInfoY.td" + //===----------------------------------------------------------------------===// // Vendor extensions //===----------------------------------------------------------------------===// diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoY.td b/llvm/lib/Target/RISCV/RISCVInstrInfoY.td new file mode 100644 index 0000000000000..bcef96050f3b3 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoY.td @@ -0,0 +1,109 @@ +//===-- 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] 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 From 02a8278c27eece95f83229126e0c812d89bd2c6f Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Sun, 3 Aug 2025 16:52:09 -0700 Subject: [PATCH 4/6] [AsmMatcher] Add support for conflicting features This helps avoiding diagnostics for instructions that could never be selected and is required for RISC-V CHERI support. 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. For RVY (CHERI), loads and stores are mode-dependent, using either a YLEN register or a XLEN register as the base. Prior to the standardization process CHERI assembly used c-prefixed register names for capabilities, so we had the following syntax for RISC-V compatible mode and CHERI pure-capability mode: lw x4, 0(c3) # capability mode: use new `CLW` tablegen instruction lw x4, 0(x3) # integer mode: use existing `LW` tablegen instruction During the standardization this was changed to keep the same register name in both modes, so now we have `lw x4, 0(x3)` in both modes but we have to select between two instructions: one using the normal GPR register class and one using the YGPR register class. We now have a choice between two instructions `LW` and `LW_Y` that have predicates that can never both be true, so we should avoid reporting missing predicates or wrong operands for the "unreachable" instruction. This change was taken from Morello LLVM with a few minor comment clarifications and changes to naming of variables. Co-authored-by: Silviu Baranga --- .../llvm/MC/MCParser/MCTargetAsmParser.h | 17 ++++++++++ .../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 9 +++++ llvm/utils/TableGen/AsmMatcherEmitter.cpp | 34 +++++++++++++++++-- 3 files changed, 57 insertions(+), 3 deletions(-) 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 365db032abdbc..fdfa0ac8f9bf5 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)) { diff --git a/llvm/utils/TableGen/AsmMatcherEmitter.cpp b/llvm/utils/TableGen/AsmMatcherEmitter.cpp index e1f2f06d755f1..ca8ea5a7955f9 100644 --- a/llvm/utils/TableGen/AsmMatcherEmitter.cpp +++ b/llvm/utils/TableGen/AsmMatcherEmitter.cpp @@ -3702,6 +3702,8 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " // Get the current feature set.\n"; OS << " const FeatureBitset &AvailableFeatures = " "getAvailableFeatures();\n\n"; + OS << " // Get the set of features to not report as missing features\n"; + OS << " const FeatureBitset &ConflictingFeatures = getConflictingFeatures();\n\n"; OS << " // Get the instruction mnemonic, which is the first token.\n"; if (HasMnemonicFirst) { @@ -3767,7 +3769,10 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { OS << " // Return a more specific error code if no mnemonics match.\n"; OS << " if (MnemonicRange.first == MnemonicRange.second)\n"; OS << " return Match_MnemonicFail;\n\n"; - + if (!ReportMultipleNearMisses) + OS << " auto FirstValidMnemonic = MnemonicRange.first;\n"; + OS << " bool FoundValidMnemonic = false;\n"; + OS << " bool HasNonConflictingMnemonic = false;\n"; OS << " for (const MatchEntry *it = MnemonicRange.first, " << "*ie = MnemonicRange.second;\n"; OS << " it != ie; ++it) {\n"; @@ -3779,7 +3784,20 @@ void AsmMatcherEmitter::run(raw_ostream &OS) { "opcode \"\n"; OS << " << MII.getName(it->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"; From 9b7a809badfd5421645f4c6dfd213e459aa3b519 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Mon, 6 Oct 2025 23:51:13 -0700 Subject: [PATCH 5/6] [RISC-V][MC] Add support for RVY loads/stores This adds supports for all new RVY loads/stores (capability-wide versions: ly/sy instructions). Additionally, for RVY (CHERI), loads and stores are mode-dependent, using either a YLEN register or a XLEN register as the base. In the former case loads/stores are authorized by that register, and in the latter (compatibility cast), the loads/stores keep using an address but are authorized by the DDC CSR. The assembler mnemonics are the same in both cases. Prior to the standardization process CHERI assembly used c-prefixed register names for capabilities, so we had the following syntax: lw x4, 0(c3) # capability mode: use new `CLW` instruction lw x4, 0(x3) # integer mode: use existing `LW` instruction During the standardization this was changed to keep the same register name in both modes, so now we have `lw x4, 0(x3)` in both modes but we have to select between two instructions: one using the normal GPR register class and one using the YGPR register class. The newly added test checks that we select the right instruction (`LW` or `LW_Y`) using --show-inst, since both the encoding and the assembler syntax are the same in both modes. This commit changes the Load_ri and Store_rri tablegen classes into a multiclass that defines the RVI and RVY at the same time to reduce the size of the diff and hopefully improve maintainability. The downstream fork had duplicated definitions which avoids merge conflicts but does mean any refactorings do not make it to the almost identical duplicate definitions. The other advantage is that we also get support for the other load/store instructions that are not explicitly tested in this commit. --- .../RISCV/Disassembler/RISCVDisassembler.cpp | 3 + llvm/lib/Target/RISCV/RISCVInstrInfo.td | 58 ++++++++++---- llvm/lib/Target/RISCV/RISCVInstrInfoF.td | 9 ++- llvm/lib/Target/RISCV/RISCVInstrInfoY.td | 6 ++ llvm/lib/Target/RISCV/RISCVInstrInfoZfh.td | 12 +-- llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td | 11 +-- llvm/lib/Target/RISCV/RISCVSchedule.td | 2 + llvm/test/MC/RISCV/rvy/rvy-valid-load-store.s | 78 +++++++++++++++++++ 8 files changed, 148 insertions(+), 31 deletions(-) create mode 100644 llvm/test/MC/RISCV/rvy/rvy-valid-load-store.s diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp index 548c4ea1c4872..1b93f5beaf2c9 100644 --- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp +++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp @@ -735,6 +735,9 @@ 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"}, diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td index da32c9c0d738f..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), 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 index bcef96050f3b3..f746075f0dfba 100644 --- a/llvm/lib/Target/RISCV/RISCVInstrInfoY.td +++ b/llvm/lib/Target/RISCV/RISCVInstrInfoY.td @@ -107,3 +107,9 @@ let Predicates = [HasStdExtY] in { 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/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/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: Date: Tue, 7 Oct 2025 00:15:00 -0700 Subject: [PATCH 6/6] [DO_NOT_MERGE][RVY] Add a call to RISCV_MC::verifyInstructionPredicates This ensures the broken Asmparser expansions trigger a crash --- .../lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp | 2 ++ llvm/test/MC/RISCV/rvy/rvy-cap-mode-invalid.s | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 llvm/test/MC/RISCV/rvy/rvy-cap-mode-invalid.s diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp index fdfa0ac8f9bf5..cd1fa5f7236bc 100644 --- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -3546,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/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