Skip to content

Conversation

jthackray
Copy link
Contributor

Some invalid assembler is currently allowed for tlbip sys alias instructions, since there's only a single table in tablegen for both tlbi and tlbip instructions. However, the latter is only a subset of the former, so invalid machine code is generated for tlbip instructions which don't actually exist.

Define a TLBI_and_TLBIP and TLBI_only enum (wish tablegen had first class enum support), so that only valid tlbip instructions are created in the TLBIPEntry table, and instructions which are not valid are correctly rejected. Add new testcases for this.

(See the Arm ARM DDI 0487 L.b pages C5-802 to 809 where valid tlbi and tlbip instructions are defined).

For example, before this change, both of the following are accepted. Subsequent to this change, tlbip is correctly rejected:

   % llvm-mc -triple=aarch64 <<< "tlbi alle1"  --show-encoding
        tlbi alle1                 // encoding: [0x9f,0x87,0x0c,0xd5]

   % llvm-mc -triple=aarch64 -mattr=+d128 <<< "tlbip alle1, x0, x1"
   <stdin>:1:7: error: invalid operand for TLBIP instruction
   tlbip alle1
         ^

@llvmbot
Copy link
Member

llvmbot commented Oct 6, 2025

@llvm/pr-subscribers-backend-aarch64

Author: Jonathan Thackray (jthackray)

Changes

Some invalid assembler is currently allowed for tlbip sys alias instructions, since there's only a single table in tablegen for both tlbi and tlbip instructions. However, the latter is only a subset of the former, so invalid machine code is generated for tlbip instructions which don't actually exist.

Define a TLBI_and_TLBIP and TLBI_only enum (wish tablegen had first class enum support), so that only valid tlbip instructions are created in the TLBIPEntry table, and instructions which are not valid are correctly rejected. Add new testcases for this.

(See the Arm ARM DDI 0487 L.b pages C5-802 to 809 where valid tlbi and tlbip instructions are defined).

For example, before this change, both of the following are accepted. Subsequent to this change, tlbip is correctly rejected:

   % llvm-mc -triple=aarch64 &lt;&lt;&lt; "tlbi alle1"  --show-encoding
        tlbi alle1                 // encoding: [0x9f,0x87,0x0c,0xd5]

   % llvm-mc -triple=aarch64 -mattr=+d128 &lt;&lt;&lt; "tlbip alle1, x0, x1"
   &lt;stdin&gt;:1:7: error: invalid operand for TLBIP instruction
   tlbip alle1
         ^

Patch is 22.96 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/162090.diff

6 Files Affected:

  • (modified) llvm/lib/Target/AArch64/AArch64SystemOperands.td (+131-90)
  • (modified) llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp (+11-11)
  • (modified) llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp (+4-3)
  • (modified) llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp (+7)
  • (modified) llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h (+8)
  • (added) llvm/test/MC/AArch64/armv9a-sysp-diagnostics.s (+95)
diff --git a/llvm/lib/Target/AArch64/AArch64SystemOperands.td b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
index 65b752ed40c90..02c90a77959ff 100644
--- a/llvm/lib/Target/AArch64/AArch64SystemOperands.td
+++ b/llvm/lib/Target/AArch64/AArch64SystemOperands.td
@@ -816,6 +816,27 @@ def : BTI<"jc", 0b110>;
 // TLBI (translation lookaside buffer invalidate) instruction options.
 //===----------------------------------------------------------------------===//
 
+class TLBImode<bit v> {
+   bit TLBI_and_TLBIP = v;
+}
+
+def TLBI_only      : TLBImode<0>;
+def TLBI_and_TLBIP : TLBImode<1>;
+
+class TLBIPEntry<string name, bits<3> op1, bits<4> crn, bits<4> crm,
+                 bits<3> op2, bit needsreg> {
+  string Name = name;
+  bits<14> Encoding;
+  let Encoding{13-11} = op1;
+  let Encoding{10-7} = crn;
+  let Encoding{6-3} = crm;
+  let Encoding{2-0} = op2;
+  bit NeedsReg = needsreg;
+  list<string> Requires = [];
+  list<string> ExtraRequires = [];
+  code RequiresStr = [{ { }] # !interleave(Requires # ExtraRequires, [{, }]) # [{ } }];
+}
+
 class TLBIEntry<string name, bits<3> op1, bits<4> crn, bits<4> crm,
              bits<3> op2, bit needsreg> {
   string Name = name;
@@ -830,131 +851,151 @@ class TLBIEntry<string name, bits<3> op1, bits<4> crn, bits<4> crm,
   code RequiresStr = [{ { }] # !interleave(Requires # ExtraRequires, [{, }]) # [{ } }];
 }
 
+def TLBIPTable : GenericTable {
+  let FilterClass = "TLBIPEntry";
+  let CppTypeName = "TLBIP";
+  let Fields = ["Name", "Encoding", "NeedsReg", "RequiresStr"];
+  let PrimaryKey = ["Encoding"];
+  let PrimaryKeyName = "lookupTLBIPByEncoding";
+}
+
 def TLBITable : GenericTable {
   let FilterClass = "TLBIEntry";
   let CppTypeName = "TLBI";
   let Fields = ["Name", "Encoding", "NeedsReg", "RequiresStr"];
-
   let PrimaryKey = ["Encoding"];
   let PrimaryKeyName = "lookupTLBIByEncoding";
 }
 
+def lookupTLBIPByName : SearchIndex {
+  let Table = TLBIPTable;
+  let Key = ["Name"];
+}
+
 def lookupTLBIByName : SearchIndex {
   let Table = TLBITable;
   let Key = ["Name"];
 }
 
-multiclass TLBI<string name, bits<3> op1, bits<4> crn, bits<4> crm,
+multiclass TLBI<string name, TLBImode mode, bits<3> op1, bits<4> crn, bits<4> crm,
              bits<3> op2, bit needsreg = 1> {
   def : TLBIEntry<name, op1, crn, crm, op2, needsreg>;
   def : TLBIEntry<!strconcat(name, "nXS"), op1, crn, crm, op2, needsreg> {
     let Encoding{7} = 1;
     let ExtraRequires = ["AArch64::FeatureXS"];
   }
+  if !eq(mode.TLBI_and_TLBIP, true) then {
+    def : TLBIPEntry<name, op1, crn, crm, op2, needsreg>;
+    def : TLBIPEntry<!strconcat(name, "nXS"), op1, crn, crm, op2, needsreg> {
+      let Encoding{7} = 1;
+      let ExtraRequires = ["AArch64::FeatureXS"];
+    }
+  }
 }
 
-defm : TLBI<"IPAS2E1IS",    0b100, 0b1000, 0b0000, 0b001>;
-defm : TLBI<"IPAS2LE1IS",   0b100, 0b1000, 0b0000, 0b101>;
-defm : TLBI<"VMALLE1IS",    0b000, 0b1000, 0b0011, 0b000, 0>;
-defm : TLBI<"ALLE2IS",      0b100, 0b1000, 0b0011, 0b000, 0>;
-defm : TLBI<"ALLE3IS",      0b110, 0b1000, 0b0011, 0b000, 0>;
-defm : TLBI<"VAE1IS",       0b000, 0b1000, 0b0011, 0b001>;
-defm : TLBI<"VAE2IS",       0b100, 0b1000, 0b0011, 0b001>;
-defm : TLBI<"VAE3IS",       0b110, 0b1000, 0b0011, 0b001>;
-defm : TLBI<"ASIDE1IS",     0b000, 0b1000, 0b0011, 0b010>;
-defm : TLBI<"VAAE1IS",      0b000, 0b1000, 0b0011, 0b011>;
-defm : TLBI<"ALLE1IS",      0b100, 0b1000, 0b0011, 0b100, 0>;
-defm : TLBI<"VALE1IS",      0b000, 0b1000, 0b0011, 0b101>;
-defm : TLBI<"VALE2IS",      0b100, 0b1000, 0b0011, 0b101>;
-defm : TLBI<"VALE3IS",      0b110, 0b1000, 0b0011, 0b101>;
-defm : TLBI<"VMALLS12E1IS", 0b100, 0b1000, 0b0011, 0b110, 0>;
-defm : TLBI<"VAALE1IS",     0b000, 0b1000, 0b0011, 0b111>;
-defm : TLBI<"IPAS2E1",      0b100, 0b1000, 0b0100, 0b001>;
-defm : TLBI<"IPAS2LE1",     0b100, 0b1000, 0b0100, 0b101>;
-defm : TLBI<"VMALLE1",      0b000, 0b1000, 0b0111, 0b000, 0>;
-defm : TLBI<"ALLE2",        0b100, 0b1000, 0b0111, 0b000, 0>;
-defm : TLBI<"ALLE3",        0b110, 0b1000, 0b0111, 0b000, 0>;
-defm : TLBI<"VAE1",         0b000, 0b1000, 0b0111, 0b001>;
-defm : TLBI<"VAE2",         0b100, 0b1000, 0b0111, 0b001>;
-defm : TLBI<"VAE3",         0b110, 0b1000, 0b0111, 0b001>;
-defm : TLBI<"ASIDE1",       0b000, 0b1000, 0b0111, 0b010>;
-defm : TLBI<"VAAE1",        0b000, 0b1000, 0b0111, 0b011>;
-defm : TLBI<"ALLE1",        0b100, 0b1000, 0b0111, 0b100, 0>;
-defm : TLBI<"VALE1",        0b000, 0b1000, 0b0111, 0b101>;
-defm : TLBI<"VALE2",        0b100, 0b1000, 0b0111, 0b101>;
-defm : TLBI<"VALE3",        0b110, 0b1000, 0b0111, 0b101>;
-defm : TLBI<"VMALLS12E1",   0b100, 0b1000, 0b0111, 0b110, 0>;
-defm : TLBI<"VAALE1",       0b000, 0b1000, 0b0111, 0b111>;
+//                          valid for       op1    CRn     CRm     op2    needsreg
+defm : TLBI<"IPAS2E1IS",    TLBI_and_TLBIP, 0b100, 0b1000, 0b0000, 0b001>;
+defm : TLBI<"IPAS2LE1IS",   TLBI_and_TLBIP, 0b100, 0b1000, 0b0000, 0b101>;
+defm : TLBI<"VMALLE1IS",    TLBI_only,      0b000, 0b1000, 0b0011, 0b000, 0>;
+defm : TLBI<"ALLE2IS",      TLBI_only,      0b100, 0b1000, 0b0011, 0b000, 0>;
+defm : TLBI<"ALLE3IS",      TLBI_only,      0b110, 0b1000, 0b0011, 0b000, 0>;
+defm : TLBI<"VAE1IS",       TLBI_and_TLBIP, 0b000, 0b1000, 0b0011, 0b001>;
+defm : TLBI<"VAE2IS",       TLBI_and_TLBIP, 0b100, 0b1000, 0b0011, 0b001>;
+defm : TLBI<"VAE3IS",       TLBI_and_TLBIP, 0b110, 0b1000, 0b0011, 0b001>;
+defm : TLBI<"ASIDE1IS",     TLBI_only,      0b000, 0b1000, 0b0011, 0b010>;
+defm : TLBI<"VAAE1IS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0011, 0b011>;
+defm : TLBI<"ALLE1IS",      TLBI_only,      0b100, 0b1000, 0b0011, 0b100, 0>;
+defm : TLBI<"VALE1IS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0011, 0b101>;
+defm : TLBI<"VALE2IS",      TLBI_and_TLBIP, 0b100, 0b1000, 0b0011, 0b101>;
+defm : TLBI<"VALE3IS",      TLBI_and_TLBIP, 0b110, 0b1000, 0b0011, 0b101>;
+defm : TLBI<"VMALLS12E1IS", TLBI_only,      0b100, 0b1000, 0b0011, 0b110, 0>;
+defm : TLBI<"VAALE1IS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0011, 0b111>;
+defm : TLBI<"IPAS2E1",      TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b001>;
+defm : TLBI<"IPAS2LE1",     TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b101>;
+defm : TLBI<"VMALLE1",      TLBI_only,      0b000, 0b1000, 0b0111, 0b000, 0>;
+defm : TLBI<"ALLE2",        TLBI_only,      0b100, 0b1000, 0b0111, 0b000, 0>;
+defm : TLBI<"ALLE3",        TLBI_only,      0b110, 0b1000, 0b0111, 0b000, 0>;
+defm : TLBI<"VAE1",         TLBI_and_TLBIP, 0b000, 0b1000, 0b0111, 0b001>;
+defm : TLBI<"VAE2",         TLBI_and_TLBIP, 0b100, 0b1000, 0b0111, 0b001>;
+defm : TLBI<"VAE3",         TLBI_and_TLBIP, 0b110, 0b1000, 0b0111, 0b001>;
+defm : TLBI<"ASIDE1",       TLBI_only,      0b000, 0b1000, 0b0111, 0b010>;
+defm : TLBI<"VAAE1",        TLBI_and_TLBIP, 0b000, 0b1000, 0b0111, 0b011>;
+defm : TLBI<"ALLE1",        TLBI_only,      0b100, 0b1000, 0b0111, 0b100, 0>;
+defm : TLBI<"VALE1",        TLBI_and_TLBIP, 0b000, 0b1000, 0b0111, 0b101>;
+defm : TLBI<"VALE2",        TLBI_and_TLBIP, 0b100, 0b1000, 0b0111, 0b101>;
+defm : TLBI<"VALE3",        TLBI_and_TLBIP, 0b110, 0b1000, 0b0111, 0b101>;
+defm : TLBI<"VMALLS12E1",   TLBI_only,      0b100, 0b1000, 0b0111, 0b110, 0>;
+defm : TLBI<"VAALE1",       TLBI_and_TLBIP, 0b000, 0b1000, 0b0111, 0b111>;
 
 // Armv8.4-A Translation Lookaside Buffer Instructions (TLBI)
 let Requires = ["AArch64::FeatureTLB_RMI"] in {
 // Armv8.4-A Outer Sharable TLB Maintenance instructions:
-//                         op1    CRn     CRm     op2
-defm : TLBI<"VMALLE1OS",    0b000, 0b1000, 0b0001, 0b000, 0>;
-defm : TLBI<"VAE1OS",       0b000, 0b1000, 0b0001, 0b001>;
-defm : TLBI<"ASIDE1OS",     0b000, 0b1000, 0b0001, 0b010>;
-defm : TLBI<"VAAE1OS",      0b000, 0b1000, 0b0001, 0b011>;
-defm : TLBI<"VALE1OS",      0b000, 0b1000, 0b0001, 0b101>;
-defm : TLBI<"VAALE1OS",     0b000, 0b1000, 0b0001, 0b111>;
-defm : TLBI<"IPAS2E1OS",    0b100, 0b1000, 0b0100, 0b000>;
-defm : TLBI<"IPAS2LE1OS",   0b100, 0b1000, 0b0100, 0b100>;
-defm : TLBI<"VAE2OS",       0b100, 0b1000, 0b0001, 0b001>;
-defm : TLBI<"VALE2OS",      0b100, 0b1000, 0b0001, 0b101>;
-defm : TLBI<"VMALLS12E1OS", 0b100, 0b1000, 0b0001, 0b110, 0>;
-defm : TLBI<"VAE3OS",       0b110, 0b1000, 0b0001, 0b001>;
-defm : TLBI<"VALE3OS",      0b110, 0b1000, 0b0001, 0b101>;
-defm : TLBI<"ALLE2OS",      0b100, 0b1000, 0b0001, 0b000, 0>;
-defm : TLBI<"ALLE1OS",      0b100, 0b1000, 0b0001, 0b100, 0>;
-defm : TLBI<"ALLE3OS",      0b110, 0b1000, 0b0001, 0b000, 0>;
+//                          valid for       op1    CRn     CRm     op2    needsreg
+defm : TLBI<"VMALLE1OS",    TLBI_only,      0b000, 0b1000, 0b0001, 0b000, 0>;
+defm : TLBI<"VAE1OS",       TLBI_and_TLBIP, 0b000, 0b1000, 0b0001, 0b001>;
+defm : TLBI<"ASIDE1OS",     TLBI_only,      0b000, 0b1000, 0b0001, 0b010>;
+defm : TLBI<"VAAE1OS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0001, 0b011>;
+defm : TLBI<"VALE1OS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0001, 0b101>;
+defm : TLBI<"VAALE1OS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0001, 0b111>;
+defm : TLBI<"IPAS2E1OS",    TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b000>;
+defm : TLBI<"IPAS2LE1OS",   TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b100>;
+defm : TLBI<"VAE2OS",       TLBI_and_TLBIP, 0b100, 0b1000, 0b0001, 0b001>;
+defm : TLBI<"VALE2OS",      TLBI_and_TLBIP, 0b100, 0b1000, 0b0001, 0b101>;
+defm : TLBI<"VMALLS12E1OS", TLBI_only,      0b100, 0b1000, 0b0001, 0b110, 0>;
+defm : TLBI<"VAE3OS",       TLBI_and_TLBIP, 0b110, 0b1000, 0b0001, 0b001>;
+defm : TLBI<"VALE3OS",      TLBI_and_TLBIP, 0b110, 0b1000, 0b0001, 0b101>;
+defm : TLBI<"ALLE2OS",      TLBI_only,      0b100, 0b1000, 0b0001, 0b000, 0>;
+defm : TLBI<"ALLE1OS",      TLBI_only,      0b100, 0b1000, 0b0001, 0b100, 0>;
+defm : TLBI<"ALLE3OS",      TLBI_only,      0b110, 0b1000, 0b0001, 0b000, 0>;
 
 // Armv8.4-A TLB Range Maintenance instructions:
-//                         op1    CRn     CRm     op2
-defm : TLBI<"RVAE1",        0b000, 0b1000, 0b0110, 0b001>;
-defm : TLBI<"RVAAE1",       0b000, 0b1000, 0b0110, 0b011>;
-defm : TLBI<"RVALE1",       0b000, 0b1000, 0b0110, 0b101>;
-defm : TLBI<"RVAALE1",      0b000, 0b1000, 0b0110, 0b111>;
-defm : TLBI<"RVAE1IS",      0b000, 0b1000, 0b0010, 0b001>;
-defm : TLBI<"RVAAE1IS",     0b000, 0b1000, 0b0010, 0b011>;
-defm : TLBI<"RVALE1IS",     0b000, 0b1000, 0b0010, 0b101>;
-defm : TLBI<"RVAALE1IS",    0b000, 0b1000, 0b0010, 0b111>;
-defm : TLBI<"RVAE1OS",      0b000, 0b1000, 0b0101, 0b001>;
-defm : TLBI<"RVAAE1OS",     0b000, 0b1000, 0b0101, 0b011>;
-defm : TLBI<"RVALE1OS",     0b000, 0b1000, 0b0101, 0b101>;
-defm : TLBI<"RVAALE1OS",    0b000, 0b1000, 0b0101, 0b111>;
-defm : TLBI<"RIPAS2E1IS",   0b100, 0b1000, 0b0000, 0b010>;
-defm : TLBI<"RIPAS2LE1IS",  0b100, 0b1000, 0b0000, 0b110>;
-defm : TLBI<"RIPAS2E1",     0b100, 0b1000, 0b0100, 0b010>;
-defm : TLBI<"RIPAS2LE1",    0b100, 0b1000, 0b0100, 0b110>;
-defm : TLBI<"RIPAS2E1OS",   0b100, 0b1000, 0b0100, 0b011>;
-defm : TLBI<"RIPAS2LE1OS",  0b100, 0b1000, 0b0100, 0b111>;
-defm : TLBI<"RVAE2",        0b100, 0b1000, 0b0110, 0b001>;
-defm : TLBI<"RVALE2",       0b100, 0b1000, 0b0110, 0b101>;
-defm : TLBI<"RVAE2IS",      0b100, 0b1000, 0b0010, 0b001>;
-defm : TLBI<"RVALE2IS",     0b100, 0b1000, 0b0010, 0b101>;
-defm : TLBI<"RVAE2OS",      0b100, 0b1000, 0b0101, 0b001>;
-defm : TLBI<"RVALE2OS",     0b100, 0b1000, 0b0101, 0b101>;
-defm : TLBI<"RVAE3",        0b110, 0b1000, 0b0110, 0b001>;
-defm : TLBI<"RVALE3",       0b110, 0b1000, 0b0110, 0b101>;
-defm : TLBI<"RVAE3IS",      0b110, 0b1000, 0b0010, 0b001>;
-defm : TLBI<"RVALE3IS",     0b110, 0b1000, 0b0010, 0b101>;
-defm : TLBI<"RVAE3OS",      0b110, 0b1000, 0b0101, 0b001>;
-defm : TLBI<"RVALE3OS",     0b110, 0b1000, 0b0101, 0b101>;
+//                          valid for       op1    CRn     CRm     op2    needsreg
+defm : TLBI<"RVAE1",        TLBI_and_TLBIP, 0b000, 0b1000, 0b0110, 0b001>;
+defm : TLBI<"RVAAE1",       TLBI_and_TLBIP, 0b000, 0b1000, 0b0110, 0b011>;
+defm : TLBI<"RVALE1",       TLBI_and_TLBIP, 0b000, 0b1000, 0b0110, 0b101>;
+defm : TLBI<"RVAALE1",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0110, 0b111>;
+defm : TLBI<"RVAE1IS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0010, 0b001>;
+defm : TLBI<"RVAAE1IS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0010, 0b011>;
+defm : TLBI<"RVALE1IS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0010, 0b101>;
+defm : TLBI<"RVAALE1IS",    TLBI_and_TLBIP, 0b000, 0b1000, 0b0010, 0b111>;
+defm : TLBI<"RVAE1OS",      TLBI_and_TLBIP, 0b000, 0b1000, 0b0101, 0b001>;
+defm : TLBI<"RVAAE1OS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0101, 0b011>;
+defm : TLBI<"RVALE1OS",     TLBI_and_TLBIP, 0b000, 0b1000, 0b0101, 0b101>;
+defm : TLBI<"RVAALE1OS",    TLBI_and_TLBIP, 0b000, 0b1000, 0b0101, 0b111>;
+defm : TLBI<"RIPAS2E1IS",   TLBI_and_TLBIP, 0b100, 0b1000, 0b0000, 0b010>;
+defm : TLBI<"RIPAS2LE1IS",  TLBI_and_TLBIP, 0b100, 0b1000, 0b0000, 0b110>;
+defm : TLBI<"RIPAS2E1",     TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b010>;
+defm : TLBI<"RIPAS2LE1",    TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b110>;
+defm : TLBI<"RIPAS2E1OS",   TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b011>;
+defm : TLBI<"RIPAS2LE1OS",  TLBI_and_TLBIP, 0b100, 0b1000, 0b0100, 0b111>;
+defm : TLBI<"RVAE2",        TLBI_and_TLBIP, 0b100, 0b1000, 0b0110, 0b001>;
+defm : TLBI<"RVALE2",       TLBI_and_TLBIP, 0b100, 0b1000, 0b0110, 0b101>;
+defm : TLBI<"RVAE2IS",      TLBI_and_TLBIP, 0b100, 0b1000, 0b0010, 0b001>;
+defm : TLBI<"RVALE2IS",     TLBI_and_TLBIP, 0b100, 0b1000, 0b0010, 0b101>;
+defm : TLBI<"RVAE2OS",      TLBI_and_TLBIP, 0b100, 0b1000, 0b0101, 0b001>;
+defm : TLBI<"RVALE2OS",     TLBI_and_TLBIP, 0b100, 0b1000, 0b0101, 0b101>;
+defm : TLBI<"RVAE3",        TLBI_and_TLBIP, 0b110, 0b1000, 0b0110, 0b001>;
+defm : TLBI<"RVALE3",       TLBI_and_TLBIP, 0b110, 0b1000, 0b0110, 0b101>;
+defm : TLBI<"RVAE3IS",      TLBI_and_TLBIP, 0b110, 0b1000, 0b0010, 0b001>;
+defm : TLBI<"RVALE3IS",     TLBI_and_TLBIP, 0b110, 0b1000, 0b0010, 0b101>;
+defm : TLBI<"RVAE3OS",      TLBI_and_TLBIP, 0b110, 0b1000, 0b0101, 0b001>;
+defm : TLBI<"RVALE3OS",     TLBI_and_TLBIP, 0b110, 0b1000, 0b0101, 0b101>;
 } //FeatureTLB_RMI
 
 // Armv9-A Realm Management Extension TLBI Instructions
 let Requires = ["AArch64::FeatureRME"] in {
-defm : TLBI<"RPAOS",        0b110, 0b1000, 0b0100, 0b011>;
-defm : TLBI<"RPALOS",       0b110, 0b1000, 0b0100, 0b111>;
-defm : TLBI<"PAALLOS",      0b110, 0b1000, 0b0001, 0b100, 0>;
-defm : TLBI<"PAALL",        0b110, 0b1000, 0b0111, 0b100, 0>;
+defm : TLBI<"RPAOS",        TLBI_only,      0b110, 0b1000, 0b0100, 0b011>;
+defm : TLBI<"RPALOS",       TLBI_only,      0b110, 0b1000, 0b0100, 0b111>;
+defm : TLBI<"PAALLOS",      TLBI_only,      0b110, 0b1000, 0b0001, 0b100, 0>;
+defm : TLBI<"PAALL",        TLBI_only,      0b110, 0b1000, 0b0111, 0b100, 0>;
 }
 
 // Armv9.5-A TLBI VMALL for Dirty State
 let Requires = ["AArch64::FeatureTLBIW"] in {
-//                           op1,   CRn,    CRm,    op2,   needsreg
-defm : TLBI<"VMALLWS2E1",    0b100, 0b1000, 0b0110, 0b010, 0>;
-defm : TLBI<"VMALLWS2E1IS",  0b100, 0b1000, 0b0010, 0b010, 0>;
-defm : TLBI<"VMALLWS2E1OS",  0b100, 0b1000, 0b0101, 0b010, 0>;
+//                                           op1,   CRn,    CRm,    op2,   needsreg
+defm : TLBI<"VMALLWS2E1",    TLBI_only,      0b100, 0b1000, 0b0110, 0b010, 0>;
+defm : TLBI<"VMALLWS2E1IS",  TLBI_only,      0b100, 0b1000, 0b0010, 0b010, 0>;
+defm : TLBI<"VMALLWS2E1OS",  TLBI_only,      0b100, 0b1000, 0b0101, 0b010, 0>;
 }
 
 //===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
index 3641e22e6f76a..2c3870c6da9b8 100644
--- a/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
+++ b/llvm/lib/Target/AArch64/AsmParser/AArch64AsmParser.cpp
@@ -4020,23 +4020,23 @@ bool AArch64AsmParser::parseSyspAlias(StringRef Name, SMLoc NameLoc,
     if (HasnXSQualifier) {
       Op = Op.drop_back(3);
     }
-    const AArch64TLBI::TLBI *TLBIorig = AArch64TLBI::lookupTLBIByName(Op);
-    if (!TLBIorig)
+    const AArch64TLBIP::TLBIP *TLBIPorig = AArch64TLBIP::lookupTLBIPByName(Op);
+    if (!TLBIPorig)
       return TokError("invalid operand for TLBIP instruction");
-    const AArch64TLBI::TLBI TLBI(
-        TLBIorig->Name, TLBIorig->Encoding | (HasnXSQualifier ? (1 << 7) : 0),
-        TLBIorig->NeedsReg,
+    const AArch64TLBIP::TLBIP TLBIP(
+        TLBIPorig->Name, TLBIPorig->Encoding | (HasnXSQualifier ? (1 << 7) : 0),
+        TLBIPorig->NeedsReg,
         HasnXSQualifier
-            ? TLBIorig->FeaturesRequired | FeatureBitset({AArch64::FeatureXS})
-            : TLBIorig->FeaturesRequired);
-    if (!TLBI.haveFeatures(getSTI().getFeatureBits())) {
+            ? TLBIPorig->FeaturesRequired | FeatureBitset({AArch64::FeatureXS})
+            : TLBIPorig->FeaturesRequired);
+    if (!TLBIP.haveFeatures(getSTI().getFeatureBits())) {
       std::string Name =
-          std::string(TLBI.Name) + (HasnXSQualifier ? "nXS" : "");
+          std::string(TLBIP.Name) + (HasnXSQualifier ? "nXS" : "");
       std::string Str("TLBIP " + Name + " requires: ");
-      setRequiredFeatureString(TLBI.getRequiredFeatures(), Str);
+      setRequiredFeatureString(TLBIP.getRequiredFeatures(), Str);
       return TokError(Str);
     }
-    createSysAlias(TLBI.Encoding, Operands, S);
+    createSysAlias(TLBIP.Encoding, Operands, S);
   }
 
   Lex(); // Eat operand.
diff --git a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
index 2552ee3009338..35bd24418f3e7 100644
--- a/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
+++ b/llvm/lib/Target/AArch64/MCTargetDesc/AArch64InstPrinter.cpp
@@ -1066,12 +1066,13 @@ bool AArch64InstPrinter::printSyspAlias(const MCInst *MI,
       Encoding &= ~(1 << 7);
     }
 
-    const AArch64TLBI::TLBI *TLBI = AArch64TLBI::lookupTLBIByEncoding(Encoding);
-    if (!TLBI || !TLBI->haveFeatures(STI.getFeatureBits()))
+    const AArch64TLBIP::TLBIP *TLBIP =
+        AArch64TLBIP::lookupTLBIPByEncoding(Encoding);
+    if (!TLBIP || !TLBIP->haveFeatures(STI.getFeatureBits()))
       return false;
 
     Ins = "tlbip\t";
-    Name = std::string(TLBI->Name);
+    Name = std::string(TLBIP->Name);
     if (CnVal == 9)
       Name += "nXS";
   } else
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
index 7767028c20bcc..d6cb0e857d57a 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.cpp
@@ -185,6 +185,13 @@ namespace llvm {
   }
 }
 
+namespace llvm {
+namespace AArch64TLBIP {
+#define GET_TLBIPTable_IMPL
+#include "AArch64GenSystemOperands.inc"
+} // namespace AArch64TLBIP
+} // namespace llvm
+
 namespace llvm {
   namespace AArch64SVCR {
 #define GET_SVCRsList_IMPL
diff --git a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
index a4ee963e2cce0..fea33ef10149d 100644
--- a/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
+++ b/llvm/lib/Target/AArch64/Utils/AArch64BaseInfo.h
@@ -795,6 +795,14 @@ namespace AArch64TLBI {
   #include "AArch64GenSystemOperands.inc"
 }
 
+namespace AArch64TLBIP {
+struct TLBIP : SysAliasReg {
+  using SysAliasReg::SysAliasReg;
+};
+#define GET_TLBIPTable_DECL
+#include "AArch64GenSystemOperands.inc"
+} // namespace AArch64TLBIP
+
 namespace AArch64II {
 /// Target Operand Flag enum.
 enum TOF {
diff --git a/llvm/test/MC/AArch64/armv9a-sysp-diagnostics.s b/llvm/test/MC/AArch64/armv9a-sysp-diagnostics.s
new file mode 100644
index 0000000000000..f8baf37d798e4
--- /dev/null
+++ b/llvm/test/MC/AArch64/armv9a-sysp-diagnostics.s
@@ -0,0 +1,95 @@
+// RUN: not llvm-mc -triple=aarch64 -show-encoding < %s 2>&1 \
+// RUN:        | FileCheck %s --check-prefixes=CHECK-ERROR
+
+tlbip ALLE1
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE1IS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE1ISNXS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE1NXS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE1OS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE1OSNXS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE2
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tlbip ALLE2IS
+// CHECK-ERROR: error: invalid operand for TLBIP instruction
+tl...
[truncated]

Some invalid assembler is currently allowed for `tlbip` sys alias
instructions, since there's only a single table in tablegen for both
`tlbi` and `tlbip` instructions. However, the latter is only a subset
of the former, so invalid machine code is generated for `tlbip`
instructions which don't actually exist.

Define a `TLBI_and_TLBIP` and `TLBI_only` enum (wish tablegen had
first class enum support), so that only valid `tlbip` instructions are
created in the `TLBIPEntry` table, and instructions which are not valid
are correctly rejected. Add new testcases for this.

(See the Arm ARM DDI 0487 L.b pages C5-802 to 809 where valid `tlbi`
and `tlbip` instructions are defined).

For example, before this change, both of the following are accepted.
Subsequent to this change, `tlbip` is correctly rejected:

```
   % llvm-mc -triple=aarch64 <<< "tlbi alle1"  --show-encoding
        tlbi alle1                 // encoding: [0x9f,0x87,0x0c,0xd5]

   % llvm-mc -triple=aarch64 -mattr=+d128 <<< "tlbip alle1, x0, x1"
   <stdin>:1:7: error: invalid operand for TLBIP instruction
   tlbip alle1
         ^
```
@jthackray jthackray force-pushed the users/jthackray/define-tlbi-tlbip-correctly branch from edea15b to 20a482b Compare October 7, 2025 15:47
Copy link
Contributor

@Lukacma Lukacma left a comment

Choose a reason for hiding this comment

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

LGTM

@jthackray jthackray merged commit 2690bb6 into main Oct 8, 2025
9 checks passed
@jthackray jthackray deleted the users/jthackray/define-tlbi-tlbip-correctly branch October 8, 2025 15:45
svkeerthy pushed a commit that referenced this pull request Oct 9, 2025
)

Some invalid assembler is currently allowed for `tlbip` sys alias
instructions, since there's only a single table in tablegen for both
`tlbi` and `tlbip` instructions. However, the latter is only a subset of
the former, so invalid machine code is generated for `tlbip`
instructions which don't actually exist.

Define a `TLBI_and_TLBIP` and `TLBI_only` enum (wish tablegen had first
class enum support), so that only valid `tlbip` instructions are created
in the `TLBIPEntry` table, and instructions which are not valid are
correctly rejected. Add new testcases for this.

(See the Arm ARM DDI 0487 L.b pages C5-802 to 809 where valid `tlbi` and
`tlbip` instructions are defined).

For example, before this change, both of the following are accepted.
Subsequent to this change, `tlbip` is correctly rejected:

```
   % llvm-mc -triple=aarch64 <<< "tlbi alle1"  --show-encoding
        tlbi alle1                 // encoding: [0x9f,0x87,0x0c,0xd5]

   % llvm-mc -triple=aarch64 -mattr=+d128 <<< "tlbip alle1, x0, x1"
   <stdin>:1:7: error: invalid operand for TLBIP instruction
   tlbip alle1
         ^
```
clingfei pushed a commit to clingfei/llvm-project that referenced this pull request Oct 10, 2025
…#162090)

Some invalid assembler is currently allowed for `tlbip` sys alias
instructions, since there's only a single table in tablegen for both
`tlbi` and `tlbip` instructions. However, the latter is only a subset of
the former, so invalid machine code is generated for `tlbip`
instructions which don't actually exist.

Define a `TLBI_and_TLBIP` and `TLBI_only` enum (wish tablegen had first
class enum support), so that only valid `tlbip` instructions are created
in the `TLBIPEntry` table, and instructions which are not valid are
correctly rejected. Add new testcases for this.

(See the Arm ARM DDI 0487 L.b pages C5-802 to 809 where valid `tlbi` and
`tlbip` instructions are defined).

For example, before this change, both of the following are accepted.
Subsequent to this change, `tlbip` is correctly rejected:

```
   % llvm-mc -triple=aarch64 <<< "tlbi alle1"  --show-encoding
        tlbi alle1                 // encoding: [0x9f,0x87,0x0c,0xd5]

   % llvm-mc -triple=aarch64 -mattr=+d128 <<< "tlbip alle1, x0, x1"
   <stdin>:1:7: error: invalid operand for TLBIP instruction
   tlbip alle1
         ^
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants