Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions llvm/include/llvm/MC/MCParser/MCTargetAsmParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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; }

Expand Down
67 changes: 66 additions & 1 deletion llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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; });
}
Expand Down Expand Up @@ -799,6 +818,29 @@ struct RISCVOperand final : public MCParsedAsmOperand {
return IsConstantImm && isInt<N>(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 <class Pred> bool isSImmPred(Pred p) const {
int64_t Imm;
if (!isExpr())
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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);
}

Expand Down
42 changes: 42 additions & 0 deletions llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 <unsigned N>
static DecodeStatus decodeUImmNonZeroOperand(MCInst &Inst, uint32_t Imm,
int64_t Address,
Expand Down Expand Up @@ -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)"},
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
29 changes: 29 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;

unsigned getYBNDSWImmOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;

unsigned getVMaskReg(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
Expand Down Expand Up @@ -718,6 +722,31 @@ uint64_t RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
return 0;
}

unsigned
RISCVMCCodeEmitter::getYBNDSWImmOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &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<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
Expand Down
29 changes: 25 additions & 4 deletions llvm/lib/Target/RISCV/RISCVFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ class RISCVExtensionBitmask<bits<3> 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<int major, int minor, string desc,
list<RISCVExtension> implies = [],
string fieldname = !subst("Feature", "Has", NAME),
string value = "true">
class RISCVExperimentalExtension<
int major, int minor, string desc, list<SubtargetFeature> implies = [],
string fieldname = !subst("Feature", "Has", NAME), string value = "true">
: RISCVExtension<major, minor, desc, implies, fieldname, value, true> {
let Experimental = true;
}
Expand Down Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

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

We renamed this to XCheriPureCap using RISCVExtension, so that it plays nice with the rest of the RISCV extension infrastructure. That enables us to do things like have XCheriot automatically imply XCheriPureCap.

Copy link

Choose a reason for hiding this comment

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

Though given the direction of the Y standard, perhaps we should invert the sense of the feature bit?

Copy link

Choose a reason for hiding this comment

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

Thoughts on this part?

Copy link
Owner Author

Choose a reason for hiding this comment

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

Hmm can't you have XCheriot imply the CapMode subtargetfeature?

Copy link
Owner Author

Choose a reason for hiding this comment

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

It likely shouldn't have a x prefix since that is for vendor-extensions and the standard will have this.

Copy link

Choose a reason for hiding this comment

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

Hmm can't you have XCheriot imply the CapMode subtargetfeature?

We could do that in code, but the generic infrastructure for RV features implying other RV features only works for things declared as standard extensions or vendor extensions, and it then enforces the naming scheme.

Copy link

@resistor resistor Oct 9, 2025

Choose a reason for hiding this comment

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

Trying to page it all back in now, I think the issue might have been that CapMode can't imply XCheri (or Y in the future) because extensions can imply features, but features can't imply extensions.

Copy link
Owner Author

@arichardson arichardson Oct 9, 2025

Choose a reason for hiding this comment

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

Ah I was not aware that upstream introduced this restriction. I'm pretty sure you used to be able to imply certain non-extension features like CapMode.

: 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
//===----------------------------------------------------------------------===//
Expand Down
77 changes: 77 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrFormatsY.td
Original file line number Diff line number Diff line change
@@ -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<XLenVT>, ImmLeaf<XLenVT, [{
return (Imm >= 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>, ImmLeaf<XLenVT, [{
return Subtarget->is64Bit() ? 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<bits<7> funct7, bits<5> funct5, bits<3> funct3,
RISCVOpcode opcode, dag outs, dag ins, string opcodestr,
string argstr>
: RVInst<outs, ins, opcodestr, argstr, [], InstFormatR> {
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<dag outs, dag ins, string opcodestr, string argstr>
: 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;
}
Loading