[llvm] [RISCV] Reject XTHeadMemIdx load-update with rd == rs1 (PR #225271)

Zeyi Xu via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 21 22:46:38 PDT 2026


https://github.com/zeyi2 updated https://github.com/llvm/llvm-project/pull/225271

>From 9c074652ccee9ebbea11905cc2cecaa47eb92f59 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <mitchell.xu2 at gmail.com>
Date: Tue, 22 Sep 2026 11:04:50 +0800
Subject: [PATCH 1/3] [RISCV] Reject XTHeadMemIdx load-update with rd == rs1

---
 .../Target/RISCV/AsmParser/RISCVAsmParser.cpp | 30 +++++++++++++++++--
 llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s | 25 ++++++++++++++--
 2 files changed, 50 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index f9c1b8275108f..2c7be2f7b809c 100644
--- a/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -4245,16 +4245,40 @@ bool RISCVAsmParser::validateInstruction(MCInst &Inst,
     }
   }
 
-  if (Opcode == RISCV::TH_LDD || Opcode == RISCV::TH_LWUD ||
-      Opcode == RISCV::TH_LWD) {
+  switch (Opcode) {
+  default:
+    break;
+  case RISCV::TH_LBIA:
+  case RISCV::TH_LBIB:
+  case RISCV::TH_LBUIA:
+  case RISCV::TH_LBUIB:
+  case RISCV::TH_LHIA:
+  case RISCV::TH_LHIB:
+  case RISCV::TH_LHUIA:
+  case RISCV::TH_LHUIB:
+  case RISCV::TH_LWIA:
+  case RISCV::TH_LWIB:
+  case RISCV::TH_LWUIA:
+  case RISCV::TH_LWUIB:
+  case RISCV::TH_LDIA:
+  case RISCV::TH_LDIB:
+    if (Inst.getOperand(0).getReg() == Inst.getOperand(2).getReg())
+      return Error(Operands[1]->getStartLoc(), "rd and rs1 must be different");
+    break;
+  case RISCV::TH_LDD:
+  case RISCV::TH_LWUD:
+  case RISCV::TH_LWD: {
     MCRegister Rd1 = Inst.getOperand(0).getReg();
     MCRegister Rd2 = Inst.getOperand(1).getReg();
     MCRegister Rs1 = Inst.getOperand(2).getReg();
-    // The encoding with rd1 == rd2 == rs1 is reserved for XTHead load pair.
+    // The encoding with overlapping rs1, rd1, and rd2 is reserved for XTHead
+    // load pair.
     if (Rs1 == Rd1 || Rs1 == Rd2 || Rd1 == Rd2) {
       SMLoc Loc = Operands[1]->getStartLoc();
       return Error(Loc, "rs1, rd1, and rd2 cannot overlap");
     }
+    break;
+  }
   }
 
   if (Opcode == RISCV::CM_MVSA01 || Opcode == RISCV::QC_CM_MVSA01) {
diff --git a/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s b/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
index 0b3d8a7040e78..e9792c0336634 100644
--- a/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
+++ b/llvm/test/MC/RISCV/rv64xtheadmemidx-invalid.s
@@ -1,5 +1,5 @@
-# RUN: not llvm-mc -triple riscv32 -mattr=+xtheadmemidx < %s 2>&1 | FileCheck %s
-# RUN: not llvm-mc -triple riscv64 -mattr=+xtheadmemidx < %s 2>&1 | FileCheck %s
+# RUN: not llvm-mc -triple riscv32 -mattr=+xtheadmemidx < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV32
+# RUN: not llvm-mc -triple riscv64 -mattr=+xtheadmemidx < %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV64
 
 th.ldia		0(a0), (a1), 0, 0  # CHECK: :[[@LINE]]:10: error: register must be a GPR
 th.ldib		a0, 2(a1), 15, 1   # CHECK: :[[@LINE]]:14: error: expected '('
@@ -11,3 +11,24 @@ th.lrw		a0, 3(a1), a2, 1   # CHECK: :[[@LINE]]:13: error: register must be a GPR
 th.lrw		a0, a1, 4(a2), 2   # CHECK: :[[@LINE]]:17: error: register must be a GPR
 th.lrh		a0, a1, a2, 5      # CHECK: :[[@LINE]]:21: error: immediate must be an integer in the range [0, 3]
 th.lrhu		a0, a1, a2, -1     # CHECK: :[[@LINE]]:22: error: immediate must be an integer in the range [0, 3]
+
+th.lbia  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lbib  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lbuia a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lbuib a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lhia  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lhib  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lhuia a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lhuib a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lwia  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lwib  a0, (a0), 0, 0     # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lwuia a0, (a0), 0, 0     # RV32: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+                            # RV64: :[[@LINE-1]]:10: error: rd and rs1 must be different
+th.lwuib a0, (a0), 0, 0     # RV32: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+                            # RV64: :[[@LINE-1]]:10: error: rd and rs1 must be different
+th.ldia  a0, (a0), 0, 0     # RV32: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+                            # RV64: :[[@LINE-1]]:10: error: rd and rs1 must be different
+th.ldib  a0, (a0), 0, 0     # RV32: :[[@LINE]]:1: error: instruction requires the following: RV64I Base Instruction Set{{$}}
+                            # RV64: :[[@LINE-1]]:10: error: rd and rs1 must be different
+th.lbia  a0, (x10), 1, 0    # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different
+th.lbia  zero, (zero), 0, 0 # CHECK: :[[@LINE]]:10: error: rd and rs1 must be different

>From 383aa6e62dcf38b30d18268e8200ff3bf08a0cb5 Mon Sep 17 00:00:00 2001
From: Zeyi Xu <mitchell.xu2 at gmail.com>
Date: Tue, 22 Sep 2026 13:34:34 +0800
Subject: [PATCH 2/3] address feedback

---
 .../RISCV/Disassembler/RISCVDisassembler.cpp  | 29 +++++++
 llvm/lib/Target/RISCV/RISCVInstrInfoXTHead.td | 12 ++-
 .../RISCV/xthead-invalid-overlap.txt          | 78 +++++++++++++++++++
 3 files changed, 117 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt

diff --git a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
index df0eeca12ddeb..b222fed6906bb 100644
--- a/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
+++ b/llvm/lib/Target/RISCV/Disassembler/RISCVDisassembler.cpp
@@ -155,6 +155,35 @@ constexpr auto DecodeGPRNoX31RegisterClass =
 constexpr auto DecodeGPRX1X5RegisterClass =
     DecodeFilteredRegisterClass<DecodeGPRRegisterClass, PredX1OrX5>;
 
+static DecodeStatus decodeTHMemIdxRs1Operand(MCInst &Inst, uint32_t RegNo,
+                                             uint64_t Address,
+                                             const MCDisassembler *Decoder) {
+  const DecodeStatus Result =
+      DecodeGPRRegisterClass(Inst, RegNo, Address, Decoder);
+  if (Result != MCDisassembler::Success)
+    return Result;
+  const MCRegister Rd = Inst.getOperand(0).getReg();
+  const MCRegister Rs1 = Inst.getOperand(Inst.getNumOperands() - 1).getReg();
+  if (Rd == Rs1)
+    return MCDisassembler::Fail;
+  return Result;
+}
+
+static DecodeStatus decodeTHMemPairRs1Operand(MCInst &Inst, uint32_t RegNo,
+                                              uint64_t Address,
+                                              const MCDisassembler *Decoder) {
+  const DecodeStatus Result =
+      DecodeGPRRegisterClass(Inst, RegNo, Address, Decoder);
+  if (Result != MCDisassembler::Success)
+    return Result;
+  const MCRegister Rd1 = Inst.getOperand(0).getReg();
+  const MCRegister Rd2 = Inst.getOperand(1).getReg();
+  const MCRegister Rs1 = Inst.getOperand(Inst.getNumOperands() - 1).getReg();
+  if (Rs1 == Rd1 || Rs1 == Rd2 || Rd1 == Rd2)
+    return MCDisassembler::Fail;
+  return Result;
+}
+
 static DecodeStatus DecodeGPRPairRegisterClass(MCInst &Inst, uint32_t RegNo,
                                                uint64_t Address,
                                                const MCDisassembler *Decoder) {
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXTHead.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXTHead.td
index bc8fc8915b649..0433a56b032d7 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXTHead.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXTHead.td
@@ -62,6 +62,14 @@ def immfour : RISCVOp {
 
 def tuimm2 : TImmLeaf<XLenVT, [{return isUInt<2>(Imm);}]>;
 
+def th_memidx_rs1 : RegisterOperand<GPR> {
+  let DecoderMethod = "decodeTHMemIdxRs1Operand";
+}
+
+def th_mempair_rs1 : RegisterOperand<GPR> {
+  let DecoderMethod = "decodeTHMemPairRs1Operand";
+}
+
 //===----------------------------------------------------------------------===//
 // Instruction class templates
 //===----------------------------------------------------------------------===//
@@ -162,7 +170,7 @@ let hasSideEffects = 0, mayLoad = 1, mayStore = 0 in
 class THLoadPair<bits<5> funct5, string opcodestr, Operand consttype>
   : RVInstRBase<0b100, OPC_CUSTOM_0,
                 (outs GPR:$rd, GPR:$rs2),
-                (ins GPR:$rs1, uimm2:$uimm2, consttype:$const3or4),
+                (ins th_mempair_rs1:$rs1, uimm2:$uimm2, consttype:$const3or4),
                  opcodestr, "$rd, $rs2, (${rs1}), $uimm2, $const3or4"> {
   bits<2> uimm2;
   bits<0> const3or4;
@@ -217,7 +225,7 @@ class THLoadIndexed<RegisterClass Ty, bits<5> funct5, string opcodestr>
 
 class THLoadUpdate<bits<5> funct5, string opcodestr>
     : RVInstIBase<0b100, OPC_CUSTOM_0, (outs GPR:$rd, GPR:$rs1_wb),
-                  (ins GPR:$rs1, simm5:$simm5, uimm2:$uimm2),
+                  (ins th_memidx_rs1:$rs1, simm5:$simm5, uimm2:$uimm2),
                   opcodestr, "$rd, (${rs1}), $simm5, $uimm2"> {
   bits<5> simm5;
   bits<2> uimm2;
diff --git a/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt b/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt
new file mode 100644
index 0000000000000..1ca353647117f
--- /dev/null
+++ b/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt
@@ -0,0 +1,78 @@
+# RUN: not llvm-mc -disassemble -triple=riscv32 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV32
+# RUN: not llvm-mc -disassemble -triple=riscv64 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV64
+
+[0x0b,0x45,0x05,0x1c]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lbia	a0, (a0), 0, 2
+
+[0x0b,0x45,0x05,0x0f]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lbib	a0, (a0), -16, 3
+
+[0x0b,0x45,0x05,0x98]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lbuia	a0, (a0), 0, 0
+
+[0x0b,0x45,0xf5,0x8a]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lbuib	a0, (a0), 15, 1
+
+[0x0b,0x45,0x05,0x3c]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lhia	a0, (a0), 0, 2
+
+[0x0b,0x45,0x05,0x2f]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lhib	a0, (a0), -16, 3
+
+[0x0b,0x45,0x05,0xb8]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lhuia	a0, (a0), 0, 0
+
+[0x0b,0x45,0xf5,0xaa]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lhuib	a0, (a0), 15, 1
+
+[0x0b,0x45,0x05,0x5c]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwia	a0, (a0), 0, 2
+
+[0x0b,0x45,0x05,0x4f]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwib	a0, (a0), -16, 3
+
+[0x0b,0x45,0x05,0xd8]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwuia	a0, (a0), 0, 0
+
+[0x0b,0x45,0xf5,0xca]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwuib	a0, (a0), 15, 1
+
+[0x0b,0x45,0x05,0x78]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.ldia	a0, (a0), 0, 0
+
+[0x0b,0x45,0xf5,0x6a]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.ldib	a0, (a0), 15, 1
+
+[0x0b,0x45,0xb5,0xe2]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwd	a0, a1, (a0), 1, 3
+
+[0x0b,0xc5,0xb5,0xf4]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.lwud	a0, a1, (a1), 2, 3
+
+[0x0b,0x45,0xa6,0xf8]
+# CHECK: warning: invalid instruction encoding
+# CHECK-NOT: th.ldd	a0, a0, (a2), 0, 4
+
+[0x0b,0xc5,0x05,0x1c]
+# RV32: th.lbia	a0, (a1), 0, 2
+# RV64: th.lbia	a0, (a1), 0, 2
+
+[0x0b,0x45,0xb6,0xe2]
+# RV32: th.lwd	a0, a1, (a2), 1, 3
+# RV64: th.lwd	a0, a1, (a2), 1, 3

>From 6702cf83ec10acc26cd1988cc78fe413d38cda6b Mon Sep 17 00:00:00 2001
From: Zeyi Xu <mitchell.xu2 at gmail.com>
Date: Tue, 22 Sep 2026 13:46:11 +0800
Subject: [PATCH 3/3] better formatting

---
 llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt b/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt
index 1ca353647117f..23d4b90807d91 100644
--- a/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt
+++ b/llvm/test/MC/Disassembler/RISCV/xthead-invalid-overlap.txt
@@ -1,5 +1,7 @@
-# RUN: not llvm-mc -disassemble -triple=riscv32 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV32
-# RUN: not llvm-mc -disassemble -triple=riscv64 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 | FileCheck %s --check-prefixes=CHECK,RV64
+# RUN: not llvm-mc -disassemble -triple=riscv32 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 \
+# RUN:     | FileCheck %s --check-prefixes=CHECK,RV32
+# RUN: not llvm-mc -disassemble -triple=riscv64 -mattr=+xtheadmemidx,+xtheadmempair %s 2>&1 \
+# RUN:     | FileCheck %s --check-prefixes=CHECK,RV64
 
 [0x0b,0x45,0x05,0x1c]
 # CHECK: warning: invalid instruction encoding



More information about the llvm-commits mailing list