[llvm] [AArch64][llvm] Allow some `tlbip` insns to be used with only +tlbid (PR #177995)
Jonathan Thackray via llvm-commits
llvm-commits at lists.llvm.org
Mon Jan 26 09:00:37 PST 2026
https://github.com/jthackray updated https://github.com/llvm/llvm-project/pull/177995
>From 190c45ed153f5a1a351d7c5abaa41a6481301838 Mon Sep 17 00:00:00 2001
From: Jonathan Thackray <jonathan.thackray at arm.com>
Date: Fri, 23 Jan 2026 17:23:39 +0000
Subject: [PATCH] [AArch64][llvm] Allow some `tlbip` insns to be used with only
+tlbid
Allow `tlbip` instructions containing *E1IS*, *E1OS*, *E2IS* or *E2OS*
to be used with `+tlbid` or `+d128`. This is because the 2025 Armv9.7-A
MemSys specification says:
```
All TLBIP *E1IS*, TLBIP*E1OS*, TLBIP*E2IS* and TLBIP*E2OS* instructions
that are currently dependent on FEAT_D128 are updated to be dependent
on FEAT_D128 or FEAT_TLBID
```
---
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 5 +-
.../AArch64/AsmParser/AArch64AsmParser.cpp | 57 ++++++++++++++++++-
.../MCTargetDesc/AArch64InstPrinter.cpp | 2 +-
.../Target/AArch64/Utils/AArch64BaseInfo.h | 26 +++++++++
llvm/test/MC/AArch64/armv9.7a-tlbip-tlbid.s | 39 +++++++++++++
.../AArch64/armv9a-tlbip-d128-diagnostics.s | 5 ++
6 files changed, 130 insertions(+), 4 deletions(-)
create mode 100644 llvm/test/MC/AArch64/armv9.7a-tlbip-tlbid.s
create mode 100644 llvm/test/MC/AArch64/armv9a-tlbip-d128-diagnostics.s
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 7f43a38f337d0..023961e6c7bcb 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -391,6 +391,9 @@ def HasLSE128 : Predicate<"Subtarget->hasLSE128()">,
AssemblerPredicateWithAll<(all_of FeatureLSE128), "lse128">;
def HasD128 : Predicate<"Subtarget->hasD128()">,
AssemblerPredicateWithAll<(all_of FeatureD128), "d128">;
+def HasD128OrTLBID : Predicate<"Subtarget->hasD128() || Subtarget->hasTLBID()">,
+ AssemblerPredicateWithAll<(any_of FeatureD128, FeatureTLBID),
+ "d128 or tlbid">;
def HasCHK : Predicate<"Subtarget->hasCHK()">,
AssemblerPredicateWithAll<(all_of FeatureCHK), "chk">;
def HasGCS : Predicate<"Subtarget->hasGCS()">,
@@ -11184,7 +11187,7 @@ let Predicates = [HasRCPC3, HasNEON] in {
//===----------------------------------------------------------------------===//
// 128-bit System Instructions (FEAT_SYSINSTR128)
//===----------------------------------------------------------------------===//
-let Predicates = [HasD128] in {
+let Predicates = [HasD128OrTLBID] in {
def SYSPxt : SystemPXtI<0, "sysp">;
def SYSPxt_XZR
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 5ef3e2e50ec86..0fce51e7814cb 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4275,13 +4275,38 @@ bool AArch64AsmParser::parseSyspAlias(StringRef Name, SMLoc NameLoc,
HasnXSQualifier
? TLBIPorig->FeaturesRequired | FeatureBitset({AArch64::FeatureXS})
: TLBIPorig->FeaturesRequired);
- if (!TLBIP.haveFeatures(getSTI().getFeatureBits())) {
+
+ if (!haveTLBIPFeatures(TLBIP, getSTI().getFeatureBits())) {
std::string Name =
std::string(TLBIP.Name) + (HasnXSQualifier ? "nXS" : "");
std::string Str("TLBIP " + Name + " requires: ");
- setRequiredFeatureString(TLBIP.getRequiredFeatures(), Str);
+ FeatureBitset Required = TLBIP.getRequiredFeatures();
+
+ // With FEAT_TLBID, the spec was updated to say:
+ // "All TLBIP *E1IS*, *E1OS*, *E2IS* and *E2OS* instructions that
+ // are currently dependent on FEAT_D128 are updated to be dependent
+ // on FEAT_D128 or FEAT_TLBID."
+ // Hence this logic here to allow either.
+ if (isTLBIP_D128OrTLBID(TLBIP.Name) && Required[AArch64::FeatureD128]) {
+ FeatureBitset RequiredWithoutD128 = Required;
+ RequiredWithoutD128.reset(AArch64::FeatureD128);
+
+ if (RequiredWithoutD128.any()) {
+ setRequiredFeatureString(RequiredWithoutD128, Str);
+ Str += ", ";
+ }
+ Str += "d128 or tlbid";
+ } else {
+ setRequiredFeatureString(Required, Str);
+ }
return TokError(Str);
}
+ if (!getSTI().hasFeature(AArch64::FeatureD128) &&
+ !getSTI().hasFeature(AArch64::FeatureTLBID)) {
+ if (isTLBIP_D128OrTLBID(TLBIP.Name))
+ return TokError("instruction requires: d128 or tlbid");
+ return TokError("instruction requires: d128");
+ }
createSysAlias(TLBIP.Encoding, Operands, S);
}
@@ -5718,6 +5743,34 @@ bool AArch64AsmParser::validateInstruction(MCInst &Inst, SMLoc &IDLoc,
}
}
+ // Allow +tlbid or +d128 for specific `TLBIP` instructions
+ if ((Inst.getOpcode() == AArch64::SYSPxt ||
+ Inst.getOpcode() == AArch64::SYSPxt_XZR) &&
+ !getSTI().hasFeature(AArch64::FeatureD128)) {
+ if (!getSTI().hasFeature(AArch64::FeatureTLBID))
+ return Error(IDLoc, "instruction requires: d128");
+
+ if (Inst.getNumOperands() < 5 || !Inst.getOperand(0).isImm() ||
+ !Inst.getOperand(1).isImm() || !Inst.getOperand(2).isImm() ||
+ !Inst.getOperand(3).isImm())
+ return Error(IDLoc, "instruction requires: d128");
+
+ // If +tlbid, check first four operands are immediates, build the
+ // system opcode encoding, and look it up in the TLBIP table
+ unsigned Op1Val = Inst.getOperand(0).getImm();
+ unsigned CnVal = Inst.getOperand(1).getImm();
+ unsigned CmVal = Inst.getOperand(2).getImm();
+ unsigned Op2Val = Inst.getOperand(3).getImm();
+ uint16_t Encoding = Op2Val | (CmVal << 3) | (CnVal << 7) | (Op1Val << 11);
+
+ const AArch64TLBIP::TLBIP *TLBIP =
+ AArch64TLBIP::lookupTLBIPByEncoding(Encoding);
+
+ if (!TLBIP || !isTLBIP_D128OrTLBID(TLBIP->Name) ||
+ !haveTLBIPFeatures(*TLBIP, getSTI().getFeatureBits()))
+ return Error(IDLoc, "instruction requires: d128");
+ }
+
// On ARM64EC, only valid registers may be used. Warn against using
// explicitly disallowed registers.
if (IsWindowsArm64EC) {
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
index 3e4c1101fb8e1..d9c0e93e2e966 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
@@ -1171,7 +1171,7 @@ bool AArch64InstPrinter::printSyspAlias(const MCInst *MI,
const AArch64TLBIP::TLBIP *TLBIP =
AArch64TLBIP::lookupTLBIPByEncoding(Encoding);
- if (!TLBIP || !TLBIP->haveFeatures(STI.getFeatureBits()))
+ if (!TLBIP || !haveTLBIPFeatures(*TLBIP, STI.getFeatureBits()))
return false;
Ins = "tlbip\t";
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
index 0c98fdc75cacd..0272038a35909 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -829,6 +829,32 @@ struct TLBIP : SysAliasOptionalReg {
#include "AArch64GenSystemOperands.inc"
} // namespace AArch64TLBIP
+inline bool isTLBIP_D128OrTLBID(StringRef Name) {
+ if (Name.ends_with_insensitive("nxs"))
+ Name = Name.drop_back(3);
+
+ return Name.ends_with("e1is") || Name.ends_with("e1os") ||
+ Name.ends_with("e2is") || Name.ends_with("e2os");
+}
+
+inline bool haveTLBIPFeatures(const AArch64TLBIP::TLBIP &TLBIP,
+ FeatureBitset ActiveFeatures) {
+ if (ActiveFeatures[AArch64::FeatureAll])
+ return true;
+
+ FeatureBitset Required = TLBIP.FeaturesRequired;
+ if (isTLBIP_D128OrTLBID(TLBIP.Name) && Required[AArch64::FeatureD128]) {
+ Required.reset(AArch64::FeatureD128);
+ if ((Required & ActiveFeatures) != Required)
+ return false;
+
+ return ActiveFeatures[AArch64::FeatureD128] ||
+ ActiveFeatures[AArch64::FeatureTLBID];
+ }
+
+ return (Required & ActiveFeatures) == Required;
+}
+
namespace AArch64MLBI {
struct MLBI : SysAliasReg {
using SysAliasReg::SysAliasReg;
diff --git a/llvm/test/MC/AArch64/armv9.7a-tlbip-tlbid.s b/llvm/test/MC/AArch64/armv9.7a-tlbip-tlbid.s
new file mode 100644
index 0000000000000..c971c26b11ac2
--- /dev/null
+++ b/llvm/test/MC/AArch64/armv9.7a-tlbip-tlbid.s
@@ -0,0 +1,39 @@
+// RUN: llvm-mc -triple=aarch64 -show-encoding -mattr=+tlbid,+tlb-rmi,+xs < %s \
+// RUN: | FileCheck %s --check-prefixes=CHECK-ENCODING,CHECK-INST
+// RUN: llvm-mc -triple=aarch64 -filetype=obj -mattr=+tlbid,+tlb-rmi,+xs < %s \
+// RUN: | llvm-objdump -d --mattr=+tlbid,+tlb-rmi,+xs --no-print-imm-hex - \
+// RUN: | FileCheck %s --check-prefix=CHECK-INST
+
+// FEAT_TLBID allows TLBIP E1/E2 IS/OS instructions without FEAT_D128.
+
+tlbip VAE1IS, x8, x9
+// CHECK-INST: tlbip vae1is, x8, x9
+// CHECK-ENCODING: encoding: [0x28,0x83,0x48,0xd5]
+
+tlbip VAE1ISNXS, x8, x9
+// CHECK-INST: tlbip vae1isnxs, x8, x9
+// CHECK-ENCODING: encoding: [0x28,0x93,0x48,0xd5]
+
+tlbip VAE1OS, x8, x9
+// CHECK-INST: tlbip vae1os, x8, x9
+// CHECK-ENCODING: encoding: [0x28,0x81,0x48,0xd5]
+
+tlbip VAE1OSNXS, x8, x9
+// CHECK-INST: tlbip vae1osnxs, x8, x9
+// CHECK-ENCODING: encoding: [0x28,0x91,0x48,0xd5]
+
+tlbip VAE2IS, x14, x15
+// CHECK-INST: tlbip vae2is, x14, x15
+// CHECK-ENCODING: encoding: [0x2e,0x83,0x4c,0xd5]
+
+tlbip VAE2ISNXS, x14, x15
+// CHECK-INST: tlbip vae2isnxs, x14, x15
+// CHECK-ENCODING: encoding: [0x2e,0x93,0x4c,0xd5]
+
+tlbip VAE2OS, x14, x15
+// CHECK-INST: tlbip vae2os, x14, x15
+// CHECK-ENCODING: encoding: [0x2e,0x81,0x4c,0xd5]
+
+tlbip VAE2OSNXS, x14, x15
+// CHECK-INST: tlbip vae2osnxs, x14, x15
+// CHECK-ENCODING: encoding: [0x2e,0x91,0x4c,0xd5]
diff --git a/llvm/test/MC/AArch64/armv9a-tlbip-d128-diagnostics.s b/llvm/test/MC/AArch64/armv9a-tlbip-d128-diagnostics.s
new file mode 100644
index 0000000000000..020b33b5d03e4
--- /dev/null
+++ b/llvm/test/MC/AArch64/armv9a-tlbip-d128-diagnostics.s
@@ -0,0 +1,5 @@
+// RUN: not llvm-mc -triple=aarch64 -mattr=+tlb-rmi,+tlbid -show-encoding %s -o - 2>&1 \
+// RUN: | FileCheck %s --check-prefix=ERRORS
+
+tlbip rvale1, x0, x1
+// ERRORS: error: instruction requires: d128
More information about the llvm-commits
mailing list