[llvm] [CodeGen] Allow negative frame indicies in Register class. (PR #164459)

Petr Penzin via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 4 11:00:16 PST 2025


https://github.com/ppenzin updated https://github.com/llvm/llvm-project/pull/164459

>From dc953025b85361a975fdf9fc78cd4d37adc01247 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Sat, 23 Aug 2025 14:58:03 -0700
Subject: [PATCH 1/8] Negative frame indicies as register.

This is used by reaching definitions analysis in order to track stores /
loads from negative frame indices.
---
 llvm/include/llvm/CodeGen/Register.h     | 12 ++++++++----
 llvm/lib/CodeGen/ReachingDefAnalysis.cpp |  3 ---
 2 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index e462a814562dc..9aa30e9f3513a 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -10,6 +10,7 @@
 #define LLVM_CODEGEN_REGISTER_H
 
 #include "llvm/MC/MCRegister.h"
+#include "llvm/Support/MathExtras.h"
 #include <cassert>
 
 namespace llvm {
@@ -35,7 +36,10 @@ class Register {
   // DenseMapInfo<unsigned> uses -1u and -2u.
   static_assert(std::numeric_limits<decltype(Reg)>::max() >= 0xFFFFFFFF,
                 "Reg isn't large enough to hold full range.");
-  static constexpr unsigned FirstStackSlot = 1u << 30;
+  static constexpr unsigned MaxFrameIndexBitwidth = 30;
+  static constexpr unsigned FirstStackSlot = 1u << MaxFrameIndexBitwidth;
+  static const unsigned StackSlotMask =
+      (unsigned)(-1) >> (CHAR_BIT * sizeof(unsigned) - MaxFrameIndexBitwidth);
   static_assert(FirstStackSlot >= MCRegister::LastPhysicalReg);
   static constexpr unsigned VirtualRegFlag = 1u << 31;
 
@@ -46,8 +50,8 @@ class Register {
 
   /// Convert a non-negative frame index to a stack slot register value.
   static Register index2StackSlot(int FI) {
-    assert(FI >= 0 && "Cannot hold a negative frame index.");
-    return Register(FI + Register::FirstStackSlot);
+    assert(isInt<30>(FI) && "Frame index must be at most 30 bit integer");
+    return Register((FI & Register::StackSlotMask) | Register::FirstStackSlot);
   }
 
   /// Return true if the specified register number is in
@@ -87,7 +91,7 @@ class Register {
   /// Compute the frame index from a register value representing a stack slot.
   int stackSlotIndex() const {
     assert(isStack() && "Not a stack slot");
-    return static_cast<int>(Reg - Register::FirstStackSlot);
+    return static_cast<int>(SignExtend64(Reg & Register::StackSlotMask, 30));
   }
 
   constexpr operator unsigned() const { return Reg; }
diff --git a/llvm/lib/CodeGen/ReachingDefAnalysis.cpp b/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
index 40a89078bcf59..61706e13b8e91 100644
--- a/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
+++ b/llvm/lib/CodeGen/ReachingDefAnalysis.cpp
@@ -193,7 +193,6 @@ void ReachingDefInfo::processDefs(MachineInstr *MI) {
   for (auto &MO : MI->operands()) {
     if (MO.isFI()) {
       int FrameIndex = MO.getIndex();
-      assert(FrameIndex >= 0 && "Can't handle negative frame indicies yet!");
       if (!isFIDef(*MI, FrameIndex, TII))
         continue;
       MBBFrameObjsReachingDefs[{MBBNumber, FrameIndex}].push_back(CurInstr);
@@ -302,8 +301,6 @@ void ReachingDefInfo::print(raw_ostream &OS) {
         Register Reg;
         if (MO.isFI()) {
           int FrameIndex = MO.getIndex();
-          assert(FrameIndex >= 0 &&
-                 "Can't handle negative frame indicies yet!");
           Reg = Register::index2StackSlot(FrameIndex);
         } else if (MO.isReg()) {
           if (MO.isDef())

>From 7b2fcda9ba02b5aa5605782a365d7683ff238a1e Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Tue, 21 Oct 2025 13:59:03 -0700
Subject: [PATCH 2/8] addressed review comments.

---
 llvm/include/llvm/CodeGen/Register.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index 9aa30e9f3513a..048366159cb2f 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -38,7 +38,7 @@ class Register {
                 "Reg isn't large enough to hold full range.");
   static constexpr unsigned MaxFrameIndexBitwidth = 30;
   static constexpr unsigned FirstStackSlot = 1u << MaxFrameIndexBitwidth;
-  static const unsigned StackSlotMask =
+  static constexpr const unsigned StackSlotMask =
       (unsigned)(-1) >> (CHAR_BIT * sizeof(unsigned) - MaxFrameIndexBitwidth);
   static_assert(FirstStackSlot >= MCRegister::LastPhysicalReg);
   static constexpr unsigned VirtualRegFlag = 1u << 31;
@@ -91,7 +91,7 @@ class Register {
   /// Compute the frame index from a register value representing a stack slot.
   int stackSlotIndex() const {
     assert(isStack() && "Not a stack slot");
-    return static_cast<int>(SignExtend64(Reg & Register::StackSlotMask, 30));
+    return static_cast<int>(SignExtend64<30>(Reg & Register::StackSlotMask));
   }
 
   constexpr operator unsigned() const { return Reg; }

>From 478c945944a6348028213929b560a0207c28b87a Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Wed, 22 Oct 2025 10:45:08 -0700
Subject: [PATCH 3/8] Renamed "FirstStackSlot" to "StackSlotZero". Simplified
 the definition of "StackSlotMask" Fixed wrong assertion. Added unit test.

---
 llvm/include/llvm/CodeGen/Register.h    | 18 ++++++++------
 llvm/unittests/CodeGen/CMakeLists.txt   |  1 +
 llvm/unittests/CodeGen/RegisterTest.cpp | 33 +++++++++++++++++++++++++
 3 files changed, 44 insertions(+), 8 deletions(-)
 create mode 100644 llvm/unittests/CodeGen/RegisterTest.cpp

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index 048366159cb2f..83bf6c8ff2fa2 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -37,21 +37,22 @@ class Register {
   static_assert(std::numeric_limits<decltype(Reg)>::max() >= 0xFFFFFFFF,
                 "Reg isn't large enough to hold full range.");
   static constexpr unsigned MaxFrameIndexBitwidth = 30;
-  static constexpr unsigned FirstStackSlot = 1u << MaxFrameIndexBitwidth;
-  static constexpr const unsigned StackSlotMask =
-      (unsigned)(-1) >> (CHAR_BIT * sizeof(unsigned) - MaxFrameIndexBitwidth);
-  static_assert(FirstStackSlot >= MCRegister::LastPhysicalReg);
+  static constexpr unsigned StackSlotZero = 1u << MaxFrameIndexBitwidth;
+  static constexpr const unsigned StackSlotMask = StackSlotZero - 1;
+  static_assert(StackSlotZero >= MCRegister::LastPhysicalReg);
   static constexpr unsigned VirtualRegFlag = 1u << 31;
 
   /// Return true if this is a stack slot.
   constexpr bool isStack() const {
-    return Register::FirstStackSlot <= Reg && Reg < Register::VirtualRegFlag;
+    return Register::StackSlotZero <= Reg && Reg < Register::VirtualRegFlag;
   }
 
   /// Convert a non-negative frame index to a stack slot register value.
   static Register index2StackSlot(int FI) {
-    assert(isInt<30>(FI) && "Frame index must be at most 30 bit integer");
-    return Register((FI & Register::StackSlotMask) | Register::FirstStackSlot);
+    unsigned FIMasked = FI & Register::StackSlotMask;
+    assert(isUInt<30>(FIMasked) &&
+           "Frame index must be at most 30 bit as an unsigned integer");
+    return Register(FIMasked | Register::StackSlotZero);
   }
 
   /// Return true if the specified register number is in
@@ -91,7 +92,8 @@ class Register {
   /// Compute the frame index from a register value representing a stack slot.
   int stackSlotIndex() const {
     assert(isStack() && "Not a stack slot");
-    return static_cast<int>(SignExtend64<30>(Reg & Register::StackSlotMask));
+    return static_cast<int>(
+        SignExtend64<MaxFrameIndexBitwidth>(Reg & Register::StackSlotMask));
   }
 
   constexpr operator unsigned() const { return Reg; }
diff --git a/llvm/unittests/CodeGen/CMakeLists.txt b/llvm/unittests/CodeGen/CMakeLists.txt
index 18332d20bf8ff..4d07462babefa 100644
--- a/llvm/unittests/CodeGen/CMakeLists.txt
+++ b/llvm/unittests/CodeGen/CMakeLists.txt
@@ -39,6 +39,7 @@ add_llvm_unittest(CodeGenTests
   MachineOperandTest.cpp
   MIR2VecTest.cpp
   RegAllocScoreTest.cpp
+  RegisterTest.cpp
   PassManagerTest.cpp
   ScalableVectorMVTsTest.cpp
   SchedBoundary.cpp
diff --git a/llvm/unittests/CodeGen/RegisterTest.cpp b/llvm/unittests/CodeGen/RegisterTest.cpp
new file mode 100644
index 0000000000000..1f61aeaac7c68
--- /dev/null
+++ b/llvm/unittests/CodeGen/RegisterTest.cpp
@@ -0,0 +1,33 @@
+//===- RegisterTest.cpp -----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/CodeGen/Register.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+TEST(RegisterTest, Idx2StackSlot) {
+  ASSERT_EQ(Register::index2StackSlot(0), Register::StackSlotZero);
+  ASSERT_EQ(Register::index2StackSlot(-1),
+            Register::StackSlotZero | Register::StackSlotMask);
+  ASSERT_EQ(Register::index2StackSlot(Register::StackSlotMask),
+            Register::StackSlotZero | Register::StackSlotMask);
+  ASSERT_EQ(Register::index2StackSlot(1), Register::StackSlotZero | 1);
+}
+
+TEST(RegisterTest, StackSlotIndex) {
+  Register Reg;
+  std::vector<int64_t> FIs = {0, 1 - 1, (1 << 29) - 1, -(1 << 29)};
+
+  for (int64_t FI : FIs) {
+    Reg = Register::index2StackSlot(FI);
+    ASSERT_EQ(Reg.stackSlotIndex(), FI);
+  }
+}
+} // end namespace

>From 695baefdbfe40f3bac58495073001d15ef661349 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Wed, 22 Oct 2025 11:51:10 -0700
Subject: [PATCH 4/8] addressed review comments.

---
 llvm/include/llvm/CodeGen/Register.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index 83bf6c8ff2fa2..dbb2b655f9358 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -50,7 +50,7 @@ class Register {
   /// Convert a non-negative frame index to a stack slot register value.
   static Register index2StackSlot(int FI) {
     unsigned FIMasked = FI & Register::StackSlotMask;
-    assert(isUInt<30>(FIMasked) &&
+    assert(isUInt<MaxFrameIndexBitwidth>(FIMasked) &&
            "Frame index must be at most 30 bit as an unsigned integer");
     return Register(FIMasked | Register::StackSlotZero);
   }

>From 29bf5cdff7515e40c902902b72e67cfb5ce95613 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Wed, 22 Oct 2025 13:06:52 -0700
Subject: [PATCH 5/8] address review comments.

---
 llvm/include/llvm/CodeGen/Register.h | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index dbb2b655f9358..4d5b9e47a97a8 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -92,8 +92,7 @@ class Register {
   /// Compute the frame index from a register value representing a stack slot.
   int stackSlotIndex() const {
     assert(isStack() && "Not a stack slot");
-    return static_cast<int>(
-        SignExtend64<MaxFrameIndexBitwidth>(Reg & Register::StackSlotMask));
+    return SignExtend32<MaxFrameIndexBitwidth>(Reg & Register::StackSlotMask);
   }
 
   constexpr operator unsigned() const { return Reg; }

>From a871d1fa03660d93a2b81b4f6aaf0347996d3db2 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 23 Oct 2025 07:22:03 -0700
Subject: [PATCH 6/8] addressed review comments.

---
 llvm/include/llvm/CodeGen/Register.h    |  4 ++--
 llvm/unittests/CodeGen/RegisterTest.cpp | 17 +++++++++--------
 2 files changed, 11 insertions(+), 10 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index 4d5b9e47a97a8..f99b0fcf78b5c 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -49,9 +49,9 @@ class Register {
 
   /// Convert a non-negative frame index to a stack slot register value.
   static Register index2StackSlot(int FI) {
-    unsigned FIMasked = FI & Register::StackSlotMask;
-    assert(isUInt<MaxFrameIndexBitwidth>(FIMasked) &&
+    assert(isInt<MaxFrameIndexBitwidth>(FI) &&
            "Frame index must be at most 30 bit as an unsigned integer");
+    unsigned FIMasked = FI & Register::StackSlotMask;
     return Register(FIMasked | Register::StackSlotZero);
   }
 
diff --git a/llvm/unittests/CodeGen/RegisterTest.cpp b/llvm/unittests/CodeGen/RegisterTest.cpp
index 1f61aeaac7c68..f4c558c727156 100644
--- a/llvm/unittests/CodeGen/RegisterTest.cpp
+++ b/llvm/unittests/CodeGen/RegisterTest.cpp
@@ -13,21 +13,22 @@ using namespace llvm;
 
 namespace {
 TEST(RegisterTest, Idx2StackSlot) {
-  ASSERT_EQ(Register::index2StackSlot(0), Register::StackSlotZero);
-  ASSERT_EQ(Register::index2StackSlot(-1),
+  EXPECT_EQ(Register::index2StackSlot(0), Register::StackSlotZero);
+  EXPECT_EQ(Register::index2StackSlot(1), Register::StackSlotZero | 1);
+  EXPECT_EQ(Register::index2StackSlot(-1),
             Register::StackSlotZero | Register::StackSlotMask);
-  ASSERT_EQ(Register::index2StackSlot(Register::StackSlotMask),
-            Register::StackSlotZero | Register::StackSlotMask);
-  ASSERT_EQ(Register::index2StackSlot(1), Register::StackSlotZero | 1);
+  // check that we do not crash on the highest possible value of frame index.
+  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot((1 << 29) - 1));
+  // check that we do not crash on the lowest possible value of frame index.
+  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot(-(1 << 29)));
 }
 
 TEST(RegisterTest, StackSlotIndex) {
-  Register Reg;
   std::vector<int64_t> FIs = {0, 1 - 1, (1 << 29) - 1, -(1 << 29)};
 
   for (int64_t FI : FIs) {
-    Reg = Register::index2StackSlot(FI);
-    ASSERT_EQ(Reg.stackSlotIndex(), FI);
+    Register Reg = Register::index2StackSlot(FI);
+    EXPECT_EQ(Reg.stackSlotIndex(), FI);
   }
 }
 } // end namespace

>From e6a016ac363e01c496e01a9304e686a4f1d9b291 Mon Sep 17 00:00:00 2001
From: Mikhail Gudim <mgudim at ventanamicro.com>
Date: Thu, 23 Oct 2025 11:33:30 -0700
Subject: [PATCH 7/8] address review comments.

---
 llvm/include/llvm/CodeGen/Register.h    | 2 +-
 llvm/unittests/CodeGen/RegisterTest.cpp | 5 +++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/Register.h b/llvm/include/llvm/CodeGen/Register.h
index f99b0fcf78b5c..790db8a11e390 100644
--- a/llvm/include/llvm/CodeGen/Register.h
+++ b/llvm/include/llvm/CodeGen/Register.h
@@ -50,7 +50,7 @@ class Register {
   /// Convert a non-negative frame index to a stack slot register value.
   static Register index2StackSlot(int FI) {
     assert(isInt<MaxFrameIndexBitwidth>(FI) &&
-           "Frame index must be at most 30 bit as an unsigned integer");
+           "Frame index must be at most 30 bits.");
     unsigned FIMasked = FI & Register::StackSlotMask;
     return Register(FIMasked | Register::StackSlotZero);
   }
diff --git a/llvm/unittests/CodeGen/RegisterTest.cpp b/llvm/unittests/CodeGen/RegisterTest.cpp
index f4c558c727156..f2fbe52bbe98c 100644
--- a/llvm/unittests/CodeGen/RegisterTest.cpp
+++ b/llvm/unittests/CodeGen/RegisterTest.cpp
@@ -24,9 +24,10 @@ TEST(RegisterTest, Idx2StackSlot) {
 }
 
 TEST(RegisterTest, StackSlotIndex) {
-  std::vector<int64_t> FIs = {0, 1 - 1, (1 << 29) - 1, -(1 << 29)};
+  int MaxPowOf2 = 1 << (Register::MaxFrameIndexBitwidth - 1);
+  std::vector<int> FIs = {0, 1 - 1, MaxPowOf2 - 1, -MaxPowOf2};
 
-  for (int64_t FI : FIs) {
+  for (int FI : FIs) {
     Register Reg = Register::index2StackSlot(FI);
     EXPECT_EQ(Reg.stackSlotIndex(), FI);
   }

>From b30b8ea6d79d8bbadbec5b6568aac99405d6af91 Mon Sep 17 00:00:00 2001
From: Petr Penzin <ppenzin at tenstorrent.com>
Date: Tue, 4 Nov 2025 12:53:39 -0600
Subject: [PATCH 8/8] Review feedback: more MaxFrameIndexBitwidth - 1

---
 llvm/unittests/CodeGen/RegisterTest.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/unittests/CodeGen/RegisterTest.cpp b/llvm/unittests/CodeGen/RegisterTest.cpp
index f2fbe52bbe98c..5c49a9911d16f 100644
--- a/llvm/unittests/CodeGen/RegisterTest.cpp
+++ b/llvm/unittests/CodeGen/RegisterTest.cpp
@@ -17,10 +17,11 @@ TEST(RegisterTest, Idx2StackSlot) {
   EXPECT_EQ(Register::index2StackSlot(1), Register::StackSlotZero | 1);
   EXPECT_EQ(Register::index2StackSlot(-1),
             Register::StackSlotZero | Register::StackSlotMask);
+  int MaxPowOf2 = 1 << (Register::MaxFrameIndexBitwidth - 1);
   // check that we do not crash on the highest possible value of frame index.
-  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot((1 << 29) - 1));
+  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot(MaxPowOf2 - 1));
   // check that we do not crash on the lowest possible value of frame index.
-  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot(-(1 << 29)));
+  EXPECT_NO_FATAL_FAILURE(Register::index2StackSlot(-MaxPowOf2));
 }
 
 TEST(RegisterTest, StackSlotIndex) {



More information about the llvm-commits mailing list