[llvm] [CodeGen] Mark read_register of allocatable physreg as live-in (PR #200825)
Adhemerval Zanella via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 2 07:58:00 PDT 2026
https://github.com/zatrazz updated https://github.com/llvm/llvm-project/pull/200825
>From f41ffadbb4f0f6b58182c5dbb498cb6c4374dace Mon Sep 17 00:00:00 2001
From: Adhemerval Zanella <zatrazz at gmail.com>
Date: Fri, 29 May 2026 15:23:11 -0300
Subject: [PATCH] [CodeGen] Mark read_register of allocatable physreg as entry
live-in
Reading an allocatable register via llvm.read_register /
llvm.read_volatile_register lowers to a COPY from a physical register that
is not defined in the function. With LLVM_ENABLE_EXPENSIVE_CHECKS (or
-verify-machineinstrs) the machine verifier rejects this as "Using an
undefined physical register" (e.g. AArch64 read-fp-reg.ll reading d5).
The value such a register holds is only well-defined on function entry, so
model the read as an entry-block live-in (the same way incoming physreg
arguments are modelled). The machine verifier only permits allocatable
live-ins on the entry block, and outside the entry block the register may be
overwritten at any point, so the read is not well-defined there: emit a
warning and lower it to undef rather than producing invalid MIR. Reserved
registers (e.g. the stack pointer) are exempt and may be read in any block.
Tests run with -verify-machineinstrs; read-reg-non-entry.ll covers the
warned, undef-lowered non-entry case.
---
.../CodeGen/SelectionDAG/SelectionDAGISel.cpp | 33 +++++++++++++++----
llvm/test/CodeGen/AArch64/read-fp-reg.ll | 2 +-
.../CodeGen/AArch64/read-reg-non-entry.ll | 32 ++++++++++++++++++
.../AArch64/write-volatile-register.ll | 2 +-
4 files changed, 61 insertions(+), 8 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/read-reg-non-entry.ll
diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
index 3dc599b556ddd..8ef6d9982a22f 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
@@ -2528,22 +2528,43 @@ void SelectionDAGISel::Select_READ_REGISTER(SDNode *Op) {
EVT VT = Op->getValueType(0);
LLT Ty = VT.isSimple() ? getLLTForMVT(VT.getSimpleVT()) : LLT();
- const MachineFunction &MF = CurDAG->getMachineFunction();
+ MachineFunction &MF = CurDAG->getMachineFunction();
Register Reg = TLI->getRegisterByName(RegStr->getString().data(), Ty, MF);
+ // An allocatable register is only well-defined on entry, so model the read
+ // as an entry-block live-in. Reading it elsewhere has no defined value (it
+ // may be overwritten at any point) and the verifier forbids allocatable
+ // live-ins outside the entry block: warn and lower to undef. Reserved
+ // registers (e.g. the stack pointer) are exempt and readable in any block.
+ const TargetRegisterInfo *TRI = MF.getSubtarget().getRegisterInfo();
+ bool IsAllocatable =
+ Reg.isPhysical() && !TRI->getReservedRegs(MF).test(Reg);
+ bool InEntryBlock = FuncInfo->MBB == &MF.front();
+
SDValue New;
- if (!Reg) {
+ if (!Reg || (IsAllocatable && !InEntryBlock)) {
const Function &Fn = MF.getFunction();
- Fn.getContext().diagnose(DiagnosticInfoGenericWithLoc(
- "invalid register \"" + Twine(RegStr->getString().data()) +
- "\" for llvm.read_register",
- Fn, Op->getDebugLoc()));
+ Fn.getContext().diagnose(
+ Reg ? DiagnosticInfoGenericWithLoc(
+ "reading allocatable register \"" +
+ Twine(RegStr->getString().data()) +
+ "\" outside the entry block is not well-defined; the "
+ "result is undefined",
+ Fn, Op->getDebugLoc(), DS_Warning)
+ : DiagnosticInfoGenericWithLoc(
+ "invalid register \"" + Twine(RegStr->getString().data()) +
+ "\" for llvm.read_register",
+ Fn, Op->getDebugLoc(), DS_Error));
New =
SDValue(CurDAG->getMachineNode(TargetOpcode::IMPLICIT_DEF, dl, VT), 0);
ReplaceUses(SDValue(Op, 1), Op->getOperand(0));
} else {
New =
CurDAG->getCopyFromReg(Op->getOperand(0), dl, Reg, Op->getValueType(0));
+ // Declare the entry-block live-in so the resulting COPY does not use an
+ // undefined physical register, which the machine verifier rejects.
+ if (IsAllocatable && !MF.front().isLiveIn(Reg))
+ MF.front().addLiveIn(Reg);
}
New->setNodeId(-1);
diff --git a/llvm/test/CodeGen/AArch64/read-fp-reg.ll b/llvm/test/CodeGen/AArch64/read-fp-reg.ll
index 853f1fc051716..02390cc30b5bb 100644
--- a/llvm/test/CodeGen/AArch64/read-fp-reg.ll
+++ b/llvm/test/CodeGen/AArch64/read-fp-reg.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
-; RUN: llc -mtriple=aarch64 -fast-isel=0 -global-isel=false < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64 -fast-isel=0 -global-isel=false -verify-machineinstrs < %s | FileCheck %s
define double @test_getRegFp_d5() {
; CHECK-LABEL: test_getRegFp_d5:
diff --git a/llvm/test/CodeGen/AArch64/read-reg-non-entry.ll b/llvm/test/CodeGen/AArch64/read-reg-non-entry.ll
new file mode 100644
index 0000000000000..0d45fd5a42340
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/read-reg-non-entry.ll
@@ -0,0 +1,32 @@
+; RUN: llc -mtriple=aarch64 -fast-isel=0 -global-isel=false -verify-machineinstrs \
+; RUN: < %s 2>&1 | FileCheck %s
+
+; CHECK: warning: {{.*}}reading allocatable register "d5" outside the entry block is not well-defined; the result is undefined
+; CHECK-NOT: error
+define double @read_fp_non_entry(i1 %c) {
+entry:
+ br i1 %c, label %then, label %else
+then:
+ %0 = call i64 @llvm.read_volatile_register.i64(metadata !0)
+ %1 = bitcast i64 %0 to double
+ ret double %1
+else:
+ ret double 0.0
+}
+
+; A reserved register (sp) is exempt: it may be read in any block without a warning.
+define i64 @read_sp_non_entry(i1 %c) {
+entry:
+ br i1 %c, label %then, label %else
+then:
+ %0 = call i64 @llvm.read_register.i64(metadata !1)
+ ret i64 %0
+else:
+ ret i64 0
+}
+
+declare i64 @llvm.read_volatile_register.i64(metadata)
+declare i64 @llvm.read_register.i64(metadata)
+
+!0 = !{!"d5"}
+!1 = !{!"sp"}
diff --git a/llvm/test/CodeGen/AArch64/write-volatile-register.ll b/llvm/test/CodeGen/AArch64/write-volatile-register.ll
index ad8292905583a..2720e90233986 100644
--- a/llvm/test/CodeGen/AArch64/write-volatile-register.ll
+++ b/llvm/test/CodeGen/AArch64/write-volatile-register.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc -mtriple=aarch64 -fast-isel=0 -global-isel=false < %s | FileCheck %s
+; RUN: llc -mtriple=aarch64 -fast-isel=0 -global-isel=false -verify-machineinstrs < %s | FileCheck %s
; Tests for llvm.write_volatile_register on AArch64.
;
More information about the llvm-commits
mailing list