[llvm] [RISC-V][MC] Reject x0 as the address/temporary register for load/store pseudos (PR #227512)

Alexander Richardson via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 29 22:07:12 PDT 2026


https://github.com/arichardson updated https://github.com/llvm/llvm-project/pull/227512

>From 9db7ace37461ca4f0ebd84d4aa804300ac6f7a1b Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Tue, 29 Sep 2026 16:23:51 -0700
Subject: [PATCH 1/2] [RISC-V][MC] Reject x0 as the address/temporary register
 for load/store pseudos

Using the zero register as the destination for integer load pseudos or as
the temporary register for floating-point load and store pseudos is
illegal since it is used to synthesize the target address, and using x0
would mean the auipc/qc.e.li result is ignored and we access an invalid
location.

This commit was created with the help of AI tools
---
 llvm/lib/Target/RISCV/RISCVInstrFormats.td |  4 +-
 llvm/test/MC/RISCV/rvi-pseudos-invalid.s   | 45 +++++++++++++++-------
 2 files changed, 33 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index b945fa957072e..0119f781832dd 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -356,9 +356,9 @@ class PseudoFloatLoad<string opcodestr, RegisterClass rdty>
   let isCodeGenOnly = 0;
 }
 
-// Pseudo store instructions.
+// Pseudo store instructions (needs a temporary register != x0).
 class PseudoStore<string opcodestr, DAGOperand rsty = GPR>
-    : Pseudo<(outs GPR:$tmp), (ins rsty:$rs, bare_symbol:$addr), [], opcodestr, "$rs, $addr, $tmp"> {
+    : Pseudo<(outs GPRNoX0:$tmp), (ins rsty:$rs, bare_symbol:$addr), [], opcodestr, "$rs, $addr, $tmp"> {
   let hasSideEffects = 0;
   let mayLoad = 0;
   let mayStore = 1;
diff --git a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
index 2a45c4f0f706b..73ee89b230847 100644
--- a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
+++ b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
@@ -1,22 +1,39 @@
-# RUN: not llvm-mc %s -triple=riscv32 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV32 %s
-# RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV64 %s
+# RUN: not llvm-mc -triple=riscv32 < %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV32 %s --implicit-check-not=error:
+# RUN: not llvm-mc -triple=riscv64 < %s 2>&1 | FileCheck --check-prefixes=CHECK,CHECK-RV64 %s --implicit-check-not=error:
 
-lga x1, 1234 # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %pcrel_hi(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %pcrel_lo(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %pcrel_hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %pcrel_lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %hi(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %lo(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
-lga x1, %lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lga x1, 1234
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %pcrel_hi(1234)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %pcrel_lo(1234)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %pcrel_hi(foo)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %pcrel_lo(foo)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %hi(1234)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %lo(1234)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %hi(foo)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
+lga x1, %lo(foo)
+# CHECK: :[[#@LINE-1]]:9: error: operand must be a bare symbol name
 
 sw a2, %hi(a_symbol), a3
 # CHECK-RV32: :[[@LINE-1]]:8: error: operand must be a bare symbol name
 # CHECK-RV64: :[[@LINE-2]]:8: error: operand must be a bare symbol name
 
-sw a2, %lo(a_symbol), a3 # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
-sw a2, %lo(a_symbol)(a4), a3 # CHECK: :[[@LINE]]:27: error: expected '%' relocation specifier
+sw a2, %lo(a_symbol), a3
+# CHECK: :[[#@LINE-1]]:8: error: operand must be a bare symbol name
+sw a2, %lo(a_symbol)(a4), a3
+# CHECK: :[[#@LINE-1]]:27: error: expected '%' relocation specifier
 
 # Too few operands must be rejected
-sw a2, a_symbol # CHECK: :[[@LINE]]:16: error: too few operands for instruction
+sw a2, a_symbol
+# CHECK: :[[#@LINE-1]]:16: error: too few operands for instruction
+
+# Zero register as the temporary for the store pseudo is also illegal
+# since that would result in the auipc result being ignored.
+sw a2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:18: error: register must be a GPR excluding zero (x0)

>From 7c7d56f8ad7ded1205c6603700a44029e3c116d0 Mon Sep 17 00:00:00 2001
From: Alex Richardson <alexrichardson at google.com>
Date: Tue, 29 Sep 2026 22:06:39 -0700
Subject: [PATCH 2/2] add support for the other pseudos too

---
 llvm/lib/Target/RISCV/RISCVInstrFormats.td   |  6 +--
 llvm/lib/Target/RISCV/RISCVInstrInfoZclsd.td | 13 ------
 llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td | 15 +++++-
 llvm/test/MC/RISCV/corev/XCVelw-invalid.s    |  3 ++
 llvm/test/MC/RISCV/rv32zilsd-invalid.s       |  2 +
 llvm/test/MC/RISCV/rvi-pseudos-invalid.s     | 48 +++++++++++++++++++-
 llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s  | 32 +++++++++++++
 7 files changed, 101 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVInstrFormats.td b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
index 0119f781832dd..8bafa12e530f2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrFormats.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrFormats.td
@@ -339,8 +339,8 @@ class PseudoQuietFCMP<DAGOperand Ty>
   let mayStore = 0;
 }
 
-// Pseudo load instructions.
-class PseudoLoad<string opcodestr, DAGOperand rdty = GPR>
+// Pseudo load instructions (needs a destination/temporary register != x0).
+class PseudoLoad<string opcodestr, DAGOperand rdty = GPRNoX0>
     : Pseudo<(outs rdty:$rd), (ins bare_symbol:$addr), [], opcodestr, "$rd, $addr"> {
   let hasSideEffects = 0;
   let mayLoad = 1;
@@ -349,7 +349,7 @@ class PseudoLoad<string opcodestr, DAGOperand rdty = GPR>
 }
 
 class PseudoFloatLoad<string opcodestr, RegisterClass rdty>
-    : Pseudo<(outs GPR:$tmp, rdty:$rd), (ins bare_symbol:$addr), [], opcodestr, "$rd, $addr, $tmp"> {
+    : Pseudo<(outs GPRNoX0:$tmp, rdty:$rd), (ins bare_symbol:$addr), [], opcodestr, "$rd, $addr, $tmp"> {
   let hasSideEffects = 0;
   let mayLoad = 1;
   let mayStore = 0;
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZclsd.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZclsd.td
index 365af11ca59fb..064295c4030c2 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZclsd.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZclsd.td
@@ -15,19 +15,6 @@
 // Instruction Class Templates
 //===----------------------------------------------------------------------===//
 
-def GPRPairNoX0RV32Operand : AsmOperandClass {
-  let Name = "GPRPairNoX0RV32";
-  let ParserMethod = "parseGPRPair<false>";
-  let PredicateMethod = "isGPRPairNoX0";
-  let RenderMethod = "addRegOperands";
-  let DiagnosticType = "InvalidGPRPairNoX0RV32";
-  let DiagnosticString = "register pair must start with an even GPR other than x0";
-}
-
-def GPRPairNoX0RV32 : RegisterOperand<GPRPairNoX0> {
-  let ParserMatchClass = GPRPairNoX0RV32Operand;
-}
-
 def GPRPairCRV32Operand : AsmOperandClass {
   let Name = "GPRPairCRV32";
   let ParserMethod = "parseGPRPair<false>";
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td
index 26b826ac6d691..693a422f5ca98 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZilsd.td
@@ -29,6 +29,19 @@ def riscv_st_rv32 : RVSDNode<"SD_RV32", SDT_RISCV_SD_RV32,
 // Instruction Class Templates
 //===----------------------------------------------------------------------===//
 
+def GPRPairNoX0RV32Operand : AsmOperandClass {
+  let Name = "GPRPairNoX0RV32";
+  let ParserMethod = "parseGPRPair<false>";
+  let PredicateMethod = "isGPRPairNoX0";
+  let RenderMethod = "addRegOperands";
+  let DiagnosticType = "InvalidGPRPairNoX0RV32";
+  let DiagnosticString = "register pair must start with an even GPR other than x0";
+}
+
+def GPRPairNoX0RV32 : RegisterOperand<GPRPairNoX0> {
+  let ParserMatchClass = GPRPairNoX0RV32Operand;
+}
+
 //===----------------------------------------------------------------------===//
 // Instructions
 //===----------------------------------------------------------------------===//
@@ -44,7 +57,7 @@ def SD_RV32 : Store_rri<0b011, "sd", GPRPairRV32>,
 //===----------------------------------------------------------------------===//
 
 let Predicates = [HasStdExtZilsd, IsRV32] in {
-def PseudoLD_RV32 : PseudoLoad<"ld", GPRPairRV32>;
+def PseudoLD_RV32 : PseudoLoad<"ld", GPRPairNoX0RV32>;
 def PseudoSD_RV32 : PseudoStore<"sd", GPRPairRV32>;
 
 // Pseudo instructions for load/store optimization with 2 separate registers
diff --git a/llvm/test/MC/RISCV/corev/XCVelw-invalid.s b/llvm/test/MC/RISCV/corev/XCVelw-invalid.s
index d917c39a61692..8dabbcd9398c9 100644
--- a/llvm/test/MC/RISCV/corev/XCVelw-invalid.s
+++ b/llvm/test/MC/RISCV/corev/XCVelw-invalid.s
@@ -24,3 +24,6 @@ cv.elw 0, 0(x6)
 
 cv.elw x0
 # CHECK-ERROR: too few operands for instruction
+
+cv.elw x0, a_symbol
+# CHECK-ERROR: register must be a GPR excluding zero (x0)
diff --git a/llvm/test/MC/RISCV/rv32zilsd-invalid.s b/llvm/test/MC/RISCV/rv32zilsd-invalid.s
index f26f3e9f06d33..2b2c1dca995db 100644
--- a/llvm/test/MC/RISCV/rv32zilsd-invalid.s
+++ b/llvm/test/MC/RISCV/rv32zilsd-invalid.s
@@ -10,3 +10,5 @@ ld t2, (4)a0 # CHECK: :[[@LINE]]:4: error: register must be even
 ld s3, (4)a0 # CHECK: :[[@LINE]]:4: error: register must be even
 sd t2, (10)s2 # CHECK: :[[@LINE]]:4: error: register must be even
 sd a7, (10)s2 # CHECK: :[[@LINE]]:4: error: register must be even
+ld x0, a_symbol # CHECK: :[[@LINE]]:4: error: register pair must start with an even GPR other than x0
+sd a2, a_symbol, x0 # CHECK: :[[@LINE]]:18: error: register must be a GPR excluding zero (x0)
diff --git a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
index 73ee89b230847..cd63584a986a3 100644
--- a/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
+++ b/llvm/test/MC/RISCV/rvi-pseudos-invalid.s
@@ -33,7 +33,53 @@ sw a2, %lo(a_symbol)(a4), a3
 sw a2, a_symbol
 # CHECK: :[[#@LINE-1]]:16: error: too few operands for instruction
 
-# Zero register as the temporary for the store pseudo is also illegal
+# Zero register as the destination/temporary for load/store pseudos is illegal
 # since that would result in the auipc result being ignored.
+lb x0, a_symbol
+# CHECK: :[[#@LINE-1]]:4: error: register must be a GPR excluding zero (x0)
+lbu x0, a_symbol
+# CHECK: :[[#@LINE-1]]:5: error: register must be a GPR excluding zero (x0)
+lh x0, a_symbol
+# CHECK: :[[#@LINE-1]]:4: error: register must be a GPR excluding zero (x0)
+lhu x0, a_symbol
+# CHECK: :[[#@LINE-1]]:5: error: register must be a GPR excluding zero (x0)
+lw x0, a_symbol
+# CHECK: :[[#@LINE-1]]:4: error: register must be a GPR excluding zero (x0)
+lwu x0, a_symbol
+# CHECK-RV32: :[[#@LINE-1]]:1: error: instruction requires the following: RV64I Base Instruction Set
+# CHECK-RV64: :[[#@LINE-2]]:5: error: register must be a GPR excluding zero (x0)
+ld x0, a_symbol
+# CHECK-RV32: :[[#@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-RV32: :[[#@LINE-2]]:1: note: instruction requires the following: RV64I Base Instruction Set
+# CHECK-RV32: :[[#@LINE-3]]:1: note: instruction requires the following: 'Zilsd' (Load/Store pair instructions)
+# CHECK-RV64: :[[#@LINE-4]]:4: error: register must be a GPR excluding zero (x0)
+
+sb a2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:18: error: register must be a GPR excluding zero (x0)
+sh a2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:18: error: register must be a GPR excluding zero (x0)
 sw a2, a_symbol, x0
 # CHECK: :[[#@LINE-1]]:18: error: register must be a GPR excluding zero (x0)
+sd a2, a_symbol, x0
+# CHECK-RV32: :[[#@LINE-1]]:1: error: invalid instruction, any one of the following would fix this:
+# CHECK-RV32: :[[#@LINE-2]]:1: note: instruction requires the following: RV64I Base Instruction Set
+# CHECK-RV32: :[[#@LINE-3]]:1: note: instruction requires the following: 'Zilsd' (Load/Store pair instructions)
+# CHECK-RV64: :[[#@LINE-4]]:18: error: register must be a GPR excluding zero (x0)
+
+.option arch, +f, +d, +q, +zfhmin
+flh fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+flw fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+fld fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+flq fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+fsh fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+fsw fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+fsd fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
+fsq fa2, a_symbol, x0
+# CHECK: :[[#@LINE-1]]:20: error: register must be a GPR excluding zero (x0)
diff --git a/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s b/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
index fcdf9570fac09..3f97bdda014d2 100644
--- a/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
+++ b/llvm/test/MC/RISCV/xqcilo-pseudos-invalid.s
@@ -74,3 +74,35 @@ qc.e.sh a0, undefined
 # CHECK-ENABLED: [[@LINE+2]]:22: error: too few operands for instruction
 # CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
 qc.e.sw a0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:9: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.lb x0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:10: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.lbu x0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:9: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.lh x0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:10: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.lhu x0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:9: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.lw x0, undefined
+
+# CHECK-ENABLED: [[@LINE+2]]:24: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.sb a0, undefined, x0
+
+# CHECK-ENABLED: [[@LINE+2]]:24: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.sh a0, undefined, x0
+
+# CHECK-ENABLED: [[@LINE+2]]:24: error: register must be a GPR excluding zero (x0)
+# CHECK-DISABLED: [[@LINE+1]]:1: error: instruction requires the following: 'Xqcilo' (Qualcomm uC Large Offset Load Store Extension)
+qc.e.sw a0, undefined, x0



More information about the llvm-commits mailing list