[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