[llvm] [AArch64][llvm] Add support for optional register in `SYS` alias instructions (PR #153905)
Jonathan Thackray via llvm-commits
llvm-commits at lists.llvm.org
Fri Aug 15 17:20:06 PDT 2025
https://github.com/jthackray updated https://github.com/llvm/llvm-project/pull/153905
>From 9a0636a4a77c000c03065ea020be01141adb3234 Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Sat, 16 Aug 2025 01:19:52 +0100
Subject: [PATCH] [AArch64][llvm] Add support for optional register in `SYS`
alias instructions
Add support for future AArch64 instructions in the `SYS` alias encoding
space which may support an optional register as an operand. For example:
```
SYS #4, c8, c5, #2, {<Xt>}
```
Currently, AArch64 `SYS` alias instructions fall into two categories:
* a register value must be present (indicated by any value except `XZR`)
* no register value must be present (this value must be `XZR`)
This is defined by the optional "needsreg" parameter in TableGen (e.g. see
the multiclass definition for TLBI), which defaults to requiring a register
operand for the instruction to be valid.
Expand the code so that if an optional register operand is specified
in TableGen, then if it's not `XZR`, encode this value in the `SYS` alias
and for disassembly, print this value if it's not `XZR`. Don't produce an
error message if the register operand is missing or unexpected, if it is
specified as an optional register.
If a mandatory or optional register is not specified in the TableGen
(i.e. no register operand should be present), then disassemble to a SYS
alias instead if the register value is not xzr/x31 (encoded as 0b11111).
For instructions taking no register operands, this is specified in the
Arm ARM, with language similar to:
```
Rt should be encoded as 0b11111. If the Rt field is not set to 0b11111,
it is CONSTRAINED UNPREDICTABLE whether:
* The instruction is UNDEFINED.
* The instruction behaves as if the Rt field is set to 0b11111.
```
and since we want to follow "should" directives, and not encourage
undefined behaviour, these are not considered valid instructions, so
should not be assembled or disassembled as such.
---
.../AArch64/AsmParser/AArch64AsmParser.cpp | 13 ++++++++-----
.../MCTargetDesc/AArch64InstPrinter.cpp | 18 +++++++++++++++---
.../lib/Target/AArch64/Utils/AArch64BaseInfo.h | 10 ++++++++++
3 files changed, 33 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 1ca61f5c6b349..763a5bfcb0c62 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -3908,6 +3908,8 @@ bool AArch64AsmParser::parseSysAlias(StringRef Name, SMLoc NameLoc,
StringRef Op = Tok.getString();
SMLoc S = Tok.getLoc();
bool ExpectRegister = true;
+ bool OptionalRegister = false;
+ bool hasAll = getSTI().hasFeature(AArch64::FeatureAll);
if (Mnemonic == "ic") {
const AArch64IC::IC *IC = AArch64IC::lookupICByName(Op);
@@ -3956,7 +3958,6 @@ bool AArch64AsmParser::parseSysAlias(StringRef Name, SMLoc NameLoc,
if (Op.lower() != "rctx")
return TokError("invalid operand for prediction restriction instruction");
- bool hasAll = getSTI().hasFeature(AArch64::FeatureAll);
bool hasPredres = hasAll || getSTI().hasFeature(AArch64::FeaturePredRes);
bool hasSpecres2 = hasAll || getSTI().hasFeature(AArch64::FeatureSPECRES2);
@@ -3989,10 +3990,12 @@ bool AArch64AsmParser::parseSysAlias(StringRef Name, SMLoc NameLoc,
HasRegister = true;
}
- if (ExpectRegister && !HasRegister)
- return TokError("specified " + Mnemonic + " op requires a register");
- else if (!ExpectRegister && HasRegister)
- return TokError("specified " + Mnemonic + " op does not use a register");
+ if (!OptionalRegister) {
+ if (ExpectRegister && !HasRegister)
+ return TokError("specified " + Mnemonic + " op requires a register");
+ else if (!ExpectRegister && HasRegister)
+ return TokError("specified " + Mnemonic + " op does not use a register");
+ }
if (parseToken(AsmToken::EndOfStatement, "unexpected token in argument list"))
return true;
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
index 3c8b5712c1f0c..ae277e5b18b8c 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
@@ -917,6 +917,7 @@ bool AArch64InstPrinter::printSysAlias(const MCInst *MI,
Encoding |= Op1Val << 11;
bool NeedsReg;
+ bool OptionalReg = false;
std::string Ins;
std::string Name;
@@ -1017,13 +1018,24 @@ bool AArch64InstPrinter::printSysAlias(const MCInst *MI,
else
return false;
+ StringRef Reg = getRegisterName(MI->getOperand(4).getReg());
+ bool NotXZR = Reg != "xzr";
+
+ // If a mandatory or optional register is not specified in the TableGen
+ // (i.e. no register operand should be present), and the register value
+ // is not xzr/x31, then disassemble to a SYS alias instead.
+ if (NotXZR && !NeedsReg && !OptionalReg)
+ return false;
+
std::string Str = Ins + Name;
llvm::transform(Str, Str.begin(), ::tolower);
O << '\t' << Str;
- if (NeedsReg) {
- O << ", ";
- printRegName(O, MI->getOperand(4).getReg());
+
+ // For optional registers, don't print the value if it's xzr/x31
+ // since this defaults to xzr/x31 if register is not specified.
+ if (NeedsReg || (OptionalReg && NotXZR)) {
+ O << ", " << Reg;
}
return true;
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
index a4ee963e2cce0..857a3c73f1cc0 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -409,6 +409,16 @@ struct SysAliasReg : SysAlias {
: SysAlias(N, E, F), NeedsReg(R) {}
};
+struct SysAliasOptionalReg : SysAlias {
+ bool NeedsReg;
+ bool OptionalReg;
+ constexpr SysAliasOptionalReg(const char *N, uint16_t E, bool R, bool O)
+ : SysAlias(N, E), NeedsReg(R), OptionalReg(O) {}
+ constexpr SysAliasOptionalReg(const char *N, uint16_t E, bool R, bool O,
+ FeatureBitset F)
+ : SysAlias(N, E, F), NeedsReg(R), OptionalReg(O) {}
+};
+
struct SysAliasImm : SysAlias {
uint16_t ImmValue;
constexpr SysAliasImm(const char *N, uint16_t E, uint16_t I)
More information about the llvm-commits
mailing list