[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