[llvm] [CHERI] Add a Support utility for determining alignment requirements of CHERI capabilities. (PR #197402)

Owen Anderson via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 7 13:04:27 PDT 2026


https://github.com/resistor updated https://github.com/llvm/llvm-project/pull/197402

>From 41338f1eeb5e9fdcca77bc1761a1888104d3007a Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Wed, 13 May 2026 11:45:22 +0200
Subject: [PATCH 1/8] [CHERI] Add a Support utility for determining alignment
 requirements of CHERI capabilities.

On CHERI systems with compressed bounds representations (which is all of them that anyone cares about today), one of the tradeoffs to achieve that compression is a requirement for larger allocations to be more highly aligned. This impacts both code generation and linking in places where globals need to be aligned and/or padded based on this requirement. The specific alignment requirements vary by capability format.

This utility provides an extensible means of representing the typical alignment-related queries using a single LUT per capability format. These LUT tables were verified against the equivalent queries against the cheri-compressed-cap reference "soft" capability implementation.
---
 .../llvm/Support/CHERICapabilityFormat.h      |  60 ++++++++
 llvm/lib/Support/CHERICapabilityFormat.cpp    | 134 ++++++++++++++++++
 llvm/lib/Support/CMakeLists.txt               |   1 +
 .../Support/CHERICapabilityFormatTest.cpp     |  91 ++++++++++++
 llvm/unittests/Support/CMakeLists.txt         |   1 +
 5 files changed, 287 insertions(+)
 create mode 100644 llvm/include/llvm/Support/CHERICapabilityFormat.h
 create mode 100644 llvm/lib/Support/CHERICapabilityFormat.cpp
 create mode 100644 llvm/unittests/Support/CHERICapabilityFormatTest.cpp

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
new file mode 100644
index 0000000000000..f3a0ca2c87bbe
--- /dev/null
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -0,0 +1,60 @@
+//===--- CHERICapabilityFormat.h --------------------------------*- C++ -*-===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_SUPPORT_CHERICAPABILITYFORMAT_H
+#define LLVM_SUPPORT_CHERICAPABILITYFORMAT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/Support/Alignment.h"
+
+#include <algorithm>
+
+namespace llvm {
+
+class CHERICapabilityFormat {
+  constexpr CHERICapabilityFormat(uint64_t AM,
+                                  ArrayRef<std::pair<uint64_t, uint64_t>> L)
+      : AddressMask(AM), LUT(L) {}
+
+  uint64_t AddressMask;
+  ArrayRef<std::pair<uint64_t, uint64_t>> LUT;
+
+public:
+  /// Returns the mask for the address bits in the capability.
+  uint64_t getAddressMask() const { return AddressMask; }
+
+  /// Returns the "alignment mask" for an allocation of size \p Length. This
+  /// mask is 0 where the capability format alignment requirements for the
+  /// address to be 0, and 1 otherwise.
+  uint64_t getAlignmentMask(uint64_t Length) const {
+    auto el = std::find_if(LUT.begin(), LUT.end(),
+                           [=](const auto &p) { return Length <= p.first; });
+    assert(el != LUT.end() && "Could not find alignment mask for length!");
+    return el->second;
+  }
+
+  /// Returns the required alignment for an allocation of size \p Length.
+  Align getRequiredAlignment(uint64_t Length) const {
+    return Align((~getAlignmentMask(Length) + 1) & getAddressMask());
+  }
+
+  /// Returns \p Length rounded up to the nearest representable allocation
+  /// length.
+  uint64_t getRepresentableLength(uint64_t Length) const {
+    uint64_t Mask = getAlignmentMask(Length);
+    return (Length + ~Mask) & Mask;
+  }
+
+  static const CHERICapabilityFormat Cheriot64;
+  static const CHERICapabilityFormat Cheri64;
+  static const CHERICapabilityFormat Cheri128;
+};
+
+} // namespace llvm
+
+#endif
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
new file mode 100644
index 0000000000000..e055b716578e5
--- /dev/null
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -0,0 +1,134 @@
+//===- CHERICapabilityFormat.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/Support/CHERICapabilityFormat.h"
+
+namespace {
+
+constexpr std::pair<uint64_t, uint64_t> Cheri64AlignmentMasks[] = {
+    {0x000000000000003F, 0x00000000FFFFFFFF},
+    {0x0000000000000078, 0x00000000FFFFFFF8},
+    {0x00000000000000F0, 0x00000000FFFFFFF0},
+    {0x00000000000001E0, 0x00000000FFFFFFE0},
+    {0x00000000000003C0, 0x00000000FFFFFFC0},
+    {0x0000000000000780, 0x00000000FFFFFF80},
+    {0x0000000000000F00, 0x00000000FFFFFF00},
+    {0x0000000000001E00, 0x00000000FFFFFE00},
+    {0x0000000000003C00, 0x00000000FFFFFC00},
+    {0x0000000000007800, 0x00000000FFFFF800},
+    {0x000000000000F000, 0x00000000FFFFF000},
+    {0x000000000001E000, 0x00000000FFFFE000},
+    {0x000000000003C000, 0x00000000FFFFC000},
+    {0x0000000000078000, 0x00000000FFFF8000},
+    {0x00000000000F0000, 0x00000000FFFF0000},
+    {0x00000000001E0000, 0x00000000FFFE0000},
+    {0x00000000003C0000, 0x00000000FFFC0000},
+    {0x0000000000780000, 0x00000000FFF80000},
+    {0x0000000000F00000, 0x00000000FFF00000},
+    {0x0000000001E00000, 0x00000000FFE00000},
+    {0x0000000003C00000, 0x00000000FFC00000},
+    {0x0000000007800000, 0x00000000FF800000},
+    {0x000000000F000000, 0x00000000FF000000},
+    {0x000000001E000000, 0x00000000FE000000},
+    {0x000000003C000000, 0x00000000FC000000},
+    {0x0000000078000000, 0x00000000F8000000},
+    {0x00000000F0000000, 0x00000000F0000000},
+    {0x00000000FFFFFFFF, 0x00000000E0000000}};
+
+static constexpr std::pair<uint64_t, uint64_t> Cheri128AlignmentMasks[] = {
+    {0x0000000000000FFF, 0xFFFFFFFFFFFFFFFF},
+    {0x0000000000001FF8, 0xFFFFFFFFFFFFFFF8},
+    {0x0000000000003FF0, 0xFFFFFFFFFFFFFFF0},
+    {0x0000000000007FE0, 0xFFFFFFFFFFFFFFE0},
+    {0x000000000000FFC0, 0xFFFFFFFFFFFFFFC0},
+    {0x000000000001FF80, 0xFFFFFFFFFFFFFF80},
+    {0x000000000003FF00, 0xFFFFFFFFFFFFFF00},
+    {0x000000000007FE00, 0xFFFFFFFFFFFFFE00},
+    {0x00000000000FFC00, 0xFFFFFFFFFFFFFC00},
+    {0x00000000001FF800, 0xFFFFFFFFFFFFF800},
+    {0x00000000003FF000, 0xFFFFFFFFFFFFF000},
+    {0x00000000007FE000, 0xFFFFFFFFFFFFE000},
+    {0x0000000000FFC000, 0xFFFFFFFFFFFFC000},
+    {0x0000000001FF8000, 0xFFFFFFFFFFFF8000},
+    {0x0000000003FF0000, 0xFFFFFFFFFFFF0000},
+    {0x0000000007FE0000, 0xFFFFFFFFFFFE0000},
+    {0x000000000FFC0000, 0xFFFFFFFFFFFC0000},
+    {0x000000001FF80000, 0xFFFFFFFFFFF80000},
+    {0x000000003FF00000, 0xFFFFFFFFFFF00000},
+    {0x000000007FE00000, 0xFFFFFFFFFFE00000},
+    {0x00000000FFC00000, 0xFFFFFFFFFFC00000},
+    {0x00000001FF800000, 0xFFFFFFFFFF800000},
+    {0x00000003FF000000, 0xFFFFFFFFFF000000},
+    {0x00000007FE000000, 0xFFFFFFFFFE000000},
+    {0x0000000FFC000000, 0xFFFFFFFFFC000000},
+    {0x0000001FF8000000, 0xFFFFFFFFF8000000},
+    {0x0000003FF0000000, 0xFFFFFFFFF0000000},
+    {0x0000007FE0000000, 0xFFFFFFFFE0000000},
+    {0x000000FFC0000000, 0xFFFFFFFFC0000000},
+    {0x000001FF80000000, 0xFFFFFFFF80000000},
+    {0x000003FF00000000, 0xFFFFFFFF00000000},
+    {0x000007FE00000000, 0xFFFFFFFE00000000},
+    {0x00000FFC00000000, 0xFFFFFFFC00000000},
+    {0x00001FF800000000, 0xFFFFFFF800000000},
+    {0x00003FF000000000, 0xFFFFFFF000000000},
+    {0x00007FE000000000, 0xFFFFFFE000000000},
+    {0x0000FFC000000000, 0xFFFFFFC000000000},
+    {0x0001FF8000000000, 0xFFFFFF8000000000},
+    {0x0003FF0000000000, 0xFFFFFF0000000000},
+    {0x0007FE0000000000, 0xFFFFFE0000000000},
+    {0x000FFC0000000000, 0xFFFFFC0000000000},
+    {0x001FF80000000000, 0xFFFFF80000000000},
+    {0x003FF00000000000, 0xFFFFF00000000000},
+    {0x007FE00000000000, 0xFFFFE00000000000},
+    {0x00FFC00000000000, 0xFFFFC00000000000},
+    {0x01FF800000000000, 0xFFFF800000000000},
+    {0x03FF000000000000, 0xFFFF000000000000},
+    {0x07FE000000000000, 0xFFFE000000000000},
+    {0x0FFC000000000000, 0xFFFC000000000000},
+    {0x1FF8000000000000, 0xFFF8000000000000},
+    {0x3FF0000000000000, 0xFFF0000000000000},
+    {0x7FE0000000000000, 0xFFE0000000000000},
+    {0xFFC0000000000000, 0xFFC0000000000000},
+    {0xFFFFFFFFFFFFFFFF, 0xFF80000000000000}};
+
+static constexpr std::pair<uint64_t, uint64_t> Cheriot64AlignmentMasks[] = {
+    {0x00000000000001FF, 0x00000000FFFFFFFF},
+    {0x00000000000003FE, 0x00000000FFFFFFFE},
+    {0x00000000000007FC, 0x00000000FFFFFFFC},
+    {0x0000000000000FF8, 0x00000000FFFFFFF8},
+    {0x0000000000001FF0, 0x00000000FFFFFFF0},
+    {0x0000000000003FE0, 0x00000000FFFFFFE0},
+    {0x0000000000007FC0, 0x00000000FFFFFFC0},
+    {0x000000000000FF80, 0x00000000FFFFFF80},
+    {0x000000000001FF00, 0x00000000FFFFFF00},
+    {0x000000000003FE00, 0x00000000FFFFFE00},
+    {0x000000000007FC00, 0x00000000FFFFFC00},
+    {0x00000000000FF800, 0x00000000FFFFF800},
+    {0x00000000001FF000, 0x00000000FFFFF000},
+    {0x00000000003FE000, 0x00000000FFFFE000},
+    {0x00000000007FC000, 0x00000000FFFFC000},
+    {0x00000000FFFFFFFF, 0x00000000FF000000},
+};
+
+} // namespace
+
+namespace llvm {
+
+const CHERICapabilityFormat CHERICapabilityFormat::Cheri64 =
+    CHERICapabilityFormat(std::numeric_limits<uint32_t>::max(),
+                          Cheri64AlignmentMasks);
+
+const CHERICapabilityFormat CHERICapabilityFormat::Cheri128 =
+    CHERICapabilityFormat(std::numeric_limits<uint64_t>::max(),
+                          Cheri128AlignmentMasks);
+
+const CHERICapabilityFormat CHERICapabilityFormat::Cheriot64 =
+    CHERICapabilityFormat(std::numeric_limits<uint32_t>::max(),
+                          Cheriot64AlignmentMasks);
+
+} // namespace llvm
diff --git a/llvm/lib/Support/CMakeLists.txt b/llvm/lib/Support/CMakeLists.txt
index 100cfb567c348..51e5085447a6f 100644
--- a/llvm/lib/Support/CMakeLists.txt
+++ b/llvm/lib/Support/CMakeLists.txt
@@ -165,6 +165,7 @@ add_llvm_component_library(LLVMSupport
   BuryPointer.cpp
   CachePruning.cpp
   Caching.cpp
+  CHERICapabilityFormat.cpp
   circular_raw_ostream.cpp
   Chrono.cpp
   COM.cpp
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
new file mode 100644
index 0000000000000..68b015c21cd28
--- /dev/null
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -0,0 +1,91 @@
+#include "llvm/Support/CHERICapabilityFormat.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+namespace {
+
+TEST(CHERICapabilityFormat, Cheri64) {
+  const auto &Cheri64 = CHERICapabilityFormat::Cheri64;
+
+  EXPECT_EQ(Cheri64.getAddressMask(), 0xFFFFFFFF);
+
+  // Lengths up to 63 are byte-aligned.
+  for (uint64_t Len = 1; Len <= 63; ++Len) {
+    EXPECT_EQ(Cheri64.getRepresentableLength(Len), Len);
+    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFFF);
+  }
+
+  // Lengths up to 120 are 8-byte-aligned.
+  for (uint64_t Len = 64; Len <= 120; ++Len) {
+    EXPECT_EQ(Cheri64.getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
+    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 8);
+    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFF8);
+  }
+
+  // Lengths up to 240 are 16-byte-aligned.
+  for (uint64_t Len = 121; Len <= 240; ++Len) {
+    EXPECT_EQ(Cheri64.getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
+    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 16);
+    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFF0);
+  }
+}
+
+TEST(CHERICapabilityFormat, Cheri128) {
+  const auto &Cheri128 = CHERICapabilityFormat::Cheri128;
+
+  EXPECT_EQ(Cheri128.getAddressMask(), 0xFFFFFFFFFFFFFFFF);
+
+  // Lengths up to 4095 are byte-aligned.
+  for (uint64_t Len = 1; Len <= 4095; ++Len) {
+    EXPECT_EQ(Cheri128.getRepresentableLength(Len), Len);
+    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFFF);
+  }
+
+  // Lengths up to 8184 are 8-byte-aligned.
+  for (uint64_t Len = 4096; Len <= 8184; ++Len) {
+    EXPECT_EQ(Cheri128.getRepresentableLength(Len),
+              (Len + 7) & 0xFFFFFFFFFFFFFFF8);
+    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 8);
+    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF8);
+  }
+
+  // Lengths up to 16368 are 16-byte-aligned.
+  for (uint64_t Len = 8185; Len <= 16368; ++Len) {
+    EXPECT_EQ(Cheri128.getRepresentableLength(Len),
+              (Len + 15) & 0xFFFFFFFFFFFFFFF0);
+    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 16);
+    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
+  }
+}
+
+TEST(CHERICapabilityFormat, Cheriot64) {
+  const auto &Cheriot64 = CHERICapabilityFormat::Cheriot64;
+
+  EXPECT_EQ(Cheriot64.getAddressMask(), 0xFFFFFFFF);
+
+  // Lengths up to 511 are byte-aligned.
+  for (uint64_t Len = 1; Len <= 511; ++Len) {
+    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), Len);
+    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFF);
+  }
+
+  // Lengths up to 1022 are 2-byte-aligned.
+  for (uint64_t Len = 512; Len <= 1022; ++Len) {
+    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), (Len + 1) & 0xFFFFFFFE);
+    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 2);
+    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFE);
+  }
+
+  // Lengths up to 1022 are 4-byte-aligned.
+  for (uint64_t Len = 1023; Len <= 2044; ++Len) {
+    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
+    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 4);
+    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFC);
+  }
+}
+
+} // namespace
diff --git a/llvm/unittests/Support/CMakeLists.txt b/llvm/unittests/Support/CMakeLists.txt
index a8bf96bbe096f..e808e668a87ad 100644
--- a/llvm/unittests/Support/CMakeLists.txt
+++ b/llvm/unittests/Support/CMakeLists.txt
@@ -21,6 +21,7 @@ add_llvm_unittest(SupportTests
   Caching.cpp
   Casting.cpp
   CheckedArithmeticTest.cpp
+  CHERICapabilityFormatTest.cpp
   Chrono.cpp
   CommandLineTest.cpp
   CompressionTest.cpp

>From 65ec545ab0f6bcd5508df34556526dd709747bab Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Mon, 1 Jun 2026 16:15:31 +0200
Subject: [PATCH 2/8] [wip] Refactor CHERICapabilityFormat to compute the
 relevant alignment requirements rather than using LUTs.

TODO: CHERI64
---
 .../llvm/Support/CHERICapabilityFormat.h      |  48 +++--
 llvm/lib/Support/CHERICapabilityFormat.cpp    | 168 ++++++------------
 .../Support/CHERICapabilityFormatTest.cpp     |   6 +-
 3 files changed, 88 insertions(+), 134 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index f3a0ca2c87bbe..d60f37967299a 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -12,31 +12,26 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/Support/Alignment.h"
 
-#include <algorithm>
-
 namespace llvm {
 
-class CHERICapabilityFormat {
-  constexpr CHERICapabilityFormat(uint64_t AM,
-                                  ArrayRef<std::pair<uint64_t, uint64_t>> L)
-      : AddressMask(AM), LUT(L) {}
+namespace CHERICapabilityFormat {
+
+class CHERICapabilityFormatImpl {
+protected:
+  constexpr CHERICapabilityFormatImpl(uint64_t AM) : AddressMask(AM) {}
 
-  uint64_t AddressMask;
-  ArrayRef<std::pair<uint64_t, uint64_t>> LUT;
+  const uint64_t AddressMask;
 
 public:
+  virtual ~CHERICapabilityFormatImpl();
+
   /// Returns the mask for the address bits in the capability.
   uint64_t getAddressMask() const { return AddressMask; }
 
   /// Returns the "alignment mask" for an allocation of size \p Length. This
   /// mask is 0 where the capability format alignment requirements for the
   /// address to be 0, and 1 otherwise.
-  uint64_t getAlignmentMask(uint64_t Length) const {
-    auto el = std::find_if(LUT.begin(), LUT.end(),
-                           [=](const auto &p) { return Length <= p.first; });
-    assert(el != LUT.end() && "Could not find alignment mask for length!");
-    return el->second;
-  }
+  virtual uint64_t getAlignmentMask(uint64_t Length) const = 0;
 
   /// Returns the required alignment for an allocation of size \p Length.
   Align getRequiredAlignment(uint64_t Length) const {
@@ -49,12 +44,31 @@ class CHERICapabilityFormat {
     uint64_t Mask = getAlignmentMask(Length);
     return (Length + ~Mask) & Mask;
   }
+};
+
+class CHERIv9CapabilityFormatImpl : public CHERICapabilityFormatImpl {
+public:
+  constexpr CHERIv9CapabilityFormatImpl(uint64_t AM, unsigned MW)
+      : CHERICapabilityFormatImpl(AM), MW(MW) {}
 
-  static const CHERICapabilityFormat Cheriot64;
-  static const CHERICapabilityFormat Cheri64;
-  static const CHERICapabilityFormat Cheri128;
+  const unsigned MW;
+
+  uint64_t getAlignmentMask(uint64_t Length) const override;
+};
+
+class CHERIoTCapabilityFormatImpl : public CHERICapabilityFormatImpl {
+public:
+  constexpr CHERIoTCapabilityFormatImpl()
+      : CHERICapabilityFormatImpl(std::numeric_limits<uint32_t>::max()) {}
+
+  uint64_t getAlignmentMask(uint64_t Length) const override;
 };
 
+const CHERIv9CapabilityFormatImpl &Cheri64();
+const CHERIv9CapabilityFormatImpl &Cheri128();
+const CHERIoTCapabilityFormatImpl &Cheriot64();
+
+} // namespace CHERICapabilityFormat
 } // namespace llvm
 
 #endif
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index e055b716578e5..3f902dbc697b5 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -7,128 +7,68 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/Support/CHERICapabilityFormat.h"
+#include "llvm/ADT/bit.h"
 
-namespace {
+namespace llvm {
+namespace CHERICapabilityFormat {
+CHERICapabilityFormatImpl::~CHERICapabilityFormatImpl() {}
 
-constexpr std::pair<uint64_t, uint64_t> Cheri64AlignmentMasks[] = {
-    {0x000000000000003F, 0x00000000FFFFFFFF},
-    {0x0000000000000078, 0x00000000FFFFFFF8},
-    {0x00000000000000F0, 0x00000000FFFFFFF0},
-    {0x00000000000001E0, 0x00000000FFFFFFE0},
-    {0x00000000000003C0, 0x00000000FFFFFFC0},
-    {0x0000000000000780, 0x00000000FFFFFF80},
-    {0x0000000000000F00, 0x00000000FFFFFF00},
-    {0x0000000000001E00, 0x00000000FFFFFE00},
-    {0x0000000000003C00, 0x00000000FFFFFC00},
-    {0x0000000000007800, 0x00000000FFFFF800},
-    {0x000000000000F000, 0x00000000FFFFF000},
-    {0x000000000001E000, 0x00000000FFFFE000},
-    {0x000000000003C000, 0x00000000FFFFC000},
-    {0x0000000000078000, 0x00000000FFFF8000},
-    {0x00000000000F0000, 0x00000000FFFF0000},
-    {0x00000000001E0000, 0x00000000FFFE0000},
-    {0x00000000003C0000, 0x00000000FFFC0000},
-    {0x0000000000780000, 0x00000000FFF80000},
-    {0x0000000000F00000, 0x00000000FFF00000},
-    {0x0000000001E00000, 0x00000000FFE00000},
-    {0x0000000003C00000, 0x00000000FFC00000},
-    {0x0000000007800000, 0x00000000FF800000},
-    {0x000000000F000000, 0x00000000FF000000},
-    {0x000000001E000000, 0x00000000FE000000},
-    {0x000000003C000000, 0x00000000FC000000},
-    {0x0000000078000000, 0x00000000F8000000},
-    {0x00000000F0000000, 0x00000000F0000000},
-    {0x00000000FFFFFFFF, 0x00000000E0000000}};
+uint64_t CHERIv9CapabilityFormatImpl::getAlignmentMask(uint64_t Length) const {
+  uint64_t MWMask = (1ULL << (MW - 4)) - 1;
+  unsigned BytePrecisionLimit = MW - 2;
 
-static constexpr std::pair<uint64_t, uint64_t> Cheri128AlignmentMasks[] = {
-    {0x0000000000000FFF, 0xFFFFFFFFFFFFFFFF},
-    {0x0000000000001FF8, 0xFFFFFFFFFFFFFFF8},
-    {0x0000000000003FF0, 0xFFFFFFFFFFFFFFF0},
-    {0x0000000000007FE0, 0xFFFFFFFFFFFFFFE0},
-    {0x000000000000FFC0, 0xFFFFFFFFFFFFFFC0},
-    {0x000000000001FF80, 0xFFFFFFFFFFFFFF80},
-    {0x000000000003FF00, 0xFFFFFFFFFFFFFF00},
-    {0x000000000007FE00, 0xFFFFFFFFFFFFFE00},
-    {0x00000000000FFC00, 0xFFFFFFFFFFFFFC00},
-    {0x00000000001FF800, 0xFFFFFFFFFFFFF800},
-    {0x00000000003FF000, 0xFFFFFFFFFFFFF000},
-    {0x00000000007FE000, 0xFFFFFFFFFFFFE000},
-    {0x0000000000FFC000, 0xFFFFFFFFFFFFC000},
-    {0x0000000001FF8000, 0xFFFFFFFFFFFF8000},
-    {0x0000000003FF0000, 0xFFFFFFFFFFFF0000},
-    {0x0000000007FE0000, 0xFFFFFFFFFFFE0000},
-    {0x000000000FFC0000, 0xFFFFFFFFFFFC0000},
-    {0x000000001FF80000, 0xFFFFFFFFFFF80000},
-    {0x000000003FF00000, 0xFFFFFFFFFFF00000},
-    {0x000000007FE00000, 0xFFFFFFFFFFE00000},
-    {0x00000000FFC00000, 0xFFFFFFFFFFC00000},
-    {0x00000001FF800000, 0xFFFFFFFFFF800000},
-    {0x00000003FF000000, 0xFFFFFFFFFF000000},
-    {0x00000007FE000000, 0xFFFFFFFFFE000000},
-    {0x0000000FFC000000, 0xFFFFFFFFFC000000},
-    {0x0000001FF8000000, 0xFFFFFFFFF8000000},
-    {0x0000003FF0000000, 0xFFFFFFFFF0000000},
-    {0x0000007FE0000000, 0xFFFFFFFFE0000000},
-    {0x000000FFC0000000, 0xFFFFFFFFC0000000},
-    {0x000001FF80000000, 0xFFFFFFFF80000000},
-    {0x000003FF00000000, 0xFFFFFFFF00000000},
-    {0x000007FE00000000, 0xFFFFFFFE00000000},
-    {0x00000FFC00000000, 0xFFFFFFFC00000000},
-    {0x00001FF800000000, 0xFFFFFFF800000000},
-    {0x00003FF000000000, 0xFFFFFFF000000000},
-    {0x00007FE000000000, 0xFFFFFFE000000000},
-    {0x0000FFC000000000, 0xFFFFFFC000000000},
-    {0x0001FF8000000000, 0xFFFFFF8000000000},
-    {0x0003FF0000000000, 0xFFFFFF0000000000},
-    {0x0007FE0000000000, 0xFFFFFE0000000000},
-    {0x000FFC0000000000, 0xFFFFFC0000000000},
-    {0x001FF80000000000, 0xFFFFF80000000000},
-    {0x003FF00000000000, 0xFFFFF00000000000},
-    {0x007FE00000000000, 0xFFFFE00000000000},
-    {0x00FFC00000000000, 0xFFFFC00000000000},
-    {0x01FF800000000000, 0xFFFF800000000000},
-    {0x03FF000000000000, 0xFFFF000000000000},
-    {0x07FE000000000000, 0xFFFE000000000000},
-    {0x0FFC000000000000, 0xFFFC000000000000},
-    {0x1FF8000000000000, 0xFFF8000000000000},
-    {0x3FF0000000000000, 0xFFF0000000000000},
-    {0x7FE0000000000000, 0xFFE0000000000000},
-    {0xFFC0000000000000, 0xFFC0000000000000},
-    {0xFFFFFFFFFFFFFFFF, 0xFF80000000000000}};
+  // A zero length could either be interpreted as 2^64 or as 0, neither of
+  // which is a length the compiler should be needing the alignment mask for.
+  assert(Length != 0 && "Alignment mask for ambiguous zero length requested");
 
-static constexpr std::pair<uint64_t, uint64_t> Cheriot64AlignmentMasks[] = {
-    {0x00000000000001FF, 0x00000000FFFFFFFF},
-    {0x00000000000003FE, 0x00000000FFFFFFFE},
-    {0x00000000000007FC, 0x00000000FFFFFFFC},
-    {0x0000000000000FF8, 0x00000000FFFFFFF8},
-    {0x0000000000001FF0, 0x00000000FFFFFFF0},
-    {0x0000000000003FE0, 0x00000000FFFFFFE0},
-    {0x0000000000007FC0, 0x00000000FFFFFFC0},
-    {0x000000000000FF80, 0x00000000FFFFFF80},
-    {0x000000000001FF00, 0x00000000FFFFFF00},
-    {0x000000000003FE00, 0x00000000FFFFFE00},
-    {0x000000000007FC00, 0x00000000FFFFFC00},
-    {0x00000000000FF800, 0x00000000FFFFF800},
-    {0x00000000001FF000, 0x00000000FFFFF000},
-    {0x00000000003FE000, 0x00000000FFFFE000},
-    {0x00000000007FC000, 0x00000000FFFFC000},
-    {0x00000000FFFFFFFF, 0x00000000FF000000},
-};
+  // Because of the IE == 0 case, lengths less than 4KB have no alignment
+  // constraint.
+  if (Length < (1ULL << BytePrecisionLimit))
+    return getAddressMask();
 
-} // namespace
+  // E_initial = 52 - CountLeadingZeros(l[64:13]) for MW=14
+  unsigned E_initial = 64 - llvm::countl_zero(Length >> (MW - 1));
 
-namespace llvm {
+  // C = 1 iff any of the Einitial + 3 least significant bits of l are
+  // non-zero and the next MW - 4 least significant bits of l are all 1.
+  unsigned LSBWidth = E_initial + 3;
+  uint64_t LSBMask = (1ULL << LSBWidth) - 1;
+  bool anyLSB = (Length & LSBMask) != 0;
+
+  Length >>= LSBWidth;
+  uint64_t nextAllOnes = (Length & MWMask) == MWMask;
+  unsigned C = anyLSB && nextAllOnes;
+
+  unsigned E = E_initial + C;
+  return getAddressMask() << (E + 3);
+}
+
+uint64_t CHERIoTCapabilityFormatImpl::getAlignmentMask(uint64_t Length) const {
+  // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
+  for (unsigned e = 0; e <= 14; ++e) {
+    if (Length <= 511ULL << e)
+      return (0xFFFFFFFFFFFFFFFFULL << e) & getAddressMask();
+  }
+
+  return (0xFFFFFFFFFFFFFFFFULL << 24) & getAddressMask();
+}
 
-const CHERICapabilityFormat CHERICapabilityFormat::Cheri64 =
-    CHERICapabilityFormat(std::numeric_limits<uint32_t>::max(),
-                          Cheri64AlignmentMasks);
+const CHERIv9CapabilityFormatImpl &Cheri64() {
+  static const CHERIv9CapabilityFormatImpl TheFormat(
+      std::numeric_limits<uint32_t>::max(), 14);
+  return TheFormat;
+}
 
-const CHERICapabilityFormat CHERICapabilityFormat::Cheri128 =
-    CHERICapabilityFormat(std::numeric_limits<uint64_t>::max(),
-                          Cheri128AlignmentMasks);
+const CHERIv9CapabilityFormatImpl &Cheri128() {
+  static const CHERIv9CapabilityFormatImpl TheFormat(
+      std::numeric_limits<uint64_t>::max(), 14);
+  return TheFormat;
+}
 
-const CHERICapabilityFormat CHERICapabilityFormat::Cheriot64 =
-    CHERICapabilityFormat(std::numeric_limits<uint32_t>::max(),
-                          Cheriot64AlignmentMasks);
+const CHERIoTCapabilityFormatImpl &Cheriot64() {
+  static const CHERIoTCapabilityFormatImpl TheFormat;
+  return TheFormat;
+}
 
+} // namespace CHERICapabilityFormat
 } // namespace llvm
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
index 68b015c21cd28..719661882aa2b 100644
--- a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -6,7 +6,7 @@ using namespace llvm;
 namespace {
 
 TEST(CHERICapabilityFormat, Cheri64) {
-  const auto &Cheri64 = CHERICapabilityFormat::Cheri64;
+  const auto &Cheri64 = CHERICapabilityFormat::Cheri64();
 
   EXPECT_EQ(Cheri64.getAddressMask(), 0xFFFFFFFF);
 
@@ -33,7 +33,7 @@ TEST(CHERICapabilityFormat, Cheri64) {
 }
 
 TEST(CHERICapabilityFormat, Cheri128) {
-  const auto &Cheri128 = CHERICapabilityFormat::Cheri128;
+  const auto &Cheri128 = CHERICapabilityFormat::Cheri128();
 
   EXPECT_EQ(Cheri128.getAddressMask(), 0xFFFFFFFFFFFFFFFF);
 
@@ -62,7 +62,7 @@ TEST(CHERICapabilityFormat, Cheri128) {
 }
 
 TEST(CHERICapabilityFormat, Cheriot64) {
-  const auto &Cheriot64 = CHERICapabilityFormat::Cheriot64;
+  const auto &Cheriot64 = CHERICapabilityFormat::Cheriot64();
 
   EXPECT_EQ(Cheriot64.getAddressMask(), 0xFFFFFFFF);
 

>From 48f3ac59b1b9ec2c2bd4d61b00542166b0269b4a Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 2 Jun 2026 10:24:05 +0200
Subject: [PATCH 3/8] Cleanup and port in RVY format support.

---
 .../llvm/Support/CHERICapabilityFormat.h      | 39 ++------
 llvm/lib/Support/CHERICapabilityFormat.cpp    | 77 ++++++++-------
 .../Support/CHERICapabilityFormatTest.cpp     | 94 +++++++++----------
 3 files changed, 92 insertions(+), 118 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index d60f37967299a..57eb1fef68bc0 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -16,17 +16,12 @@ namespace llvm {
 
 namespace CHERICapabilityFormat {
 
-class CHERICapabilityFormatImpl {
-protected:
-  constexpr CHERICapabilityFormatImpl(uint64_t AM) : AddressMask(AM) {}
-
-  const uint64_t AddressMask;
-
+template <typename AddressType> class CHERICapabilityFormatImpl {
 public:
-  virtual ~CHERICapabilityFormatImpl();
+  virtual ~CHERICapabilityFormatImpl() {}
 
-  /// Returns the mask for the address bits in the capability.
-  uint64_t getAddressMask() const { return AddressMask; }
+  static constexpr uint64_t AddressMask =
+      std::numeric_limits<AddressType>::max();
 
   /// Returns the "alignment mask" for an allocation of size \p Length. This
   /// mask is 0 where the capability format alignment requirements for the
@@ -35,7 +30,7 @@ class CHERICapabilityFormatImpl {
 
   /// Returns the required alignment for an allocation of size \p Length.
   Align getRequiredAlignment(uint64_t Length) const {
-    return Align((~getAlignmentMask(Length) + 1) & getAddressMask());
+    return Align((~getAlignmentMask(Length) + 1) & AddressMask);
   }
 
   /// Returns \p Length rounded up to the nearest representable allocation
@@ -46,27 +41,9 @@ class CHERICapabilityFormatImpl {
   }
 };
 
-class CHERIv9CapabilityFormatImpl : public CHERICapabilityFormatImpl {
-public:
-  constexpr CHERIv9CapabilityFormatImpl(uint64_t AM, unsigned MW)
-      : CHERICapabilityFormatImpl(AM), MW(MW) {}
-
-  const unsigned MW;
-
-  uint64_t getAlignmentMask(uint64_t Length) const override;
-};
-
-class CHERIoTCapabilityFormatImpl : public CHERICapabilityFormatImpl {
-public:
-  constexpr CHERIoTCapabilityFormatImpl()
-      : CHERICapabilityFormatImpl(std::numeric_limits<uint32_t>::max()) {}
-
-  uint64_t getAlignmentMask(uint64_t Length) const override;
-};
-
-const CHERIv9CapabilityFormatImpl &Cheri64();
-const CHERIv9CapabilityFormatImpl &Cheri128();
-const CHERIoTCapabilityFormatImpl &Cheriot64();
+const CHERICapabilityFormatImpl<uint32_t> &RV32Y();
+const CHERICapabilityFormatImpl<uint64_t> &RV64Y();
+const CHERICapabilityFormatImpl<uint32_t> &CHERIoT();
 
 } // namespace CHERICapabilityFormat
 } // namespace llvm
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index 3f902dbc697b5..9f66db8e2a102 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -11,61 +11,58 @@
 
 namespace llvm {
 namespace CHERICapabilityFormat {
-CHERICapabilityFormatImpl::~CHERICapabilityFormatImpl() {}
 
-uint64_t CHERIv9CapabilityFormatImpl::getAlignmentMask(uint64_t Length) const {
-  uint64_t MWMask = (1ULL << (MW - 4)) - 1;
-  unsigned BytePrecisionLimit = MW - 2;
+template <typename AddressType, unsigned MW, unsigned MAX_E>
+class RVYCapabilityFormatImpl : public CHERICapabilityFormatImpl<AddressType> {
+  uint64_t getAlignmentMask(uint64_t Length) const override {
+    static constexpr unsigned int IE_TAKE_BITS = 3;
 
-  // A zero length could either be interpreted as 2^64 or as 0, neither of
-  // which is a length the compiler should be needing the alignment mask for.
-  assert(Length != 0 && "Alignment mask for ambiguous zero length requested");
+    if (Length == 0)
+      return ~static_cast<AddressType>(0) & this->AddressMask;
 
-  // Because of the IE == 0 case, lengths less than 4KB have no alignment
-  // constraint.
-  if (Length < (1ULL << BytePrecisionLimit))
-    return getAddressMask();
+    // Extract bits that overflow the uncompressed mantissa window.
+    uint64_t Slice = static_cast<uint64_t>(Length) >> (MW - 1);
+    unsigned int E = 64 - llvm::countl_zero(Slice);
+    // We use internal exponent if length overflows OR the denormal boundary
+    // bit is set.
+    bool IE = (E != 0) || ((static_cast<uint64_t>(Length) >> (MW - 2)) & 1);
+    // Include bits use by the internal exponent for the shift value.
+    unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
 
-  // E_initial = 52 - CountLeadingZeros(l[64:13]) for MW=14
-  unsigned E_initial = 64 - llvm::countl_zero(Length >> (MW - 1));
+    // These are probably not needed, but were useful for debugging when I
+    // initially got it wrong.
+    assert(E <= MAX_E && "Raw exponent exceeds architecture maximum");
+    assert(Eprime <= sizeof(AddressType) * 8 &&
+           "Shift amount exceeds integer width");
 
-  // C = 1 iff any of the Einitial + 3 least significant bits of l are
-  // non-zero and the next MW - 4 least significant bits of l are all 1.
-  unsigned LSBWidth = E_initial + 3;
-  uint64_t LSBMask = (1ULL << LSBWidth) - 1;
-  bool anyLSB = (Length & LSBMask) != 0;
-
-  Length >>= LSBWidth;
-  uint64_t nextAllOnes = (Length & MWMask) == MWMask;
-  unsigned C = anyLSB && nextAllOnes;
+    // Left-shift ~0 to mask out the lost precision bits
+    return (~static_cast<AddressType>(0) << Eprime) & this->AddressMask;
+  }
+};
 
-  unsigned E = E_initial + C;
-  return getAddressMask() << (E + 3);
-}
+class CHERIoTCapabilityFormatImpl : public CHERICapabilityFormatImpl<uint32_t> {
+  uint64_t getAlignmentMask(uint64_t Length) const override {
+    // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
+    for (unsigned e = 0; e <= 14; ++e) {
+      if (Length <= 511ULL << e)
+        return (0xFFFFFFFFFFFFFFFFULL << e) & this->AddressMask;
+    }
 
-uint64_t CHERIoTCapabilityFormatImpl::getAlignmentMask(uint64_t Length) const {
-  // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
-  for (unsigned e = 0; e <= 14; ++e) {
-    if (Length <= 511ULL << e)
-      return (0xFFFFFFFFFFFFFFFFULL << e) & getAddressMask();
+    return (0xFFFFFFFFFFFFFFFFULL << 24) & this->AddressMask;
   }
+};
 
-  return (0xFFFFFFFFFFFFFFFFULL << 24) & getAddressMask();
-}
-
-const CHERIv9CapabilityFormatImpl &Cheri64() {
-  static const CHERIv9CapabilityFormatImpl TheFormat(
-      std::numeric_limits<uint32_t>::max(), 14);
+const CHERICapabilityFormatImpl<uint32_t> &RV32Y() {
+  static const RVYCapabilityFormatImpl<uint32_t, 14, 52> TheFormat;
   return TheFormat;
 }
 
-const CHERIv9CapabilityFormatImpl &Cheri128() {
-  static const CHERIv9CapabilityFormatImpl TheFormat(
-      std::numeric_limits<uint64_t>::max(), 14);
+const CHERICapabilityFormatImpl<uint64_t> &RV64Y() {
+  static const RVYCapabilityFormatImpl<uint64_t, 14, 24> TheFormat;
   return TheFormat;
 }
 
-const CHERIoTCapabilityFormatImpl &Cheriot64() {
+const CHERICapabilityFormatImpl<uint32_t> &CHERIoT() {
   static const CHERIoTCapabilityFormatImpl TheFormat;
   return TheFormat;
 }
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
index 719661882aa2b..249670519c966 100644
--- a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -5,86 +5,86 @@ using namespace llvm;
 
 namespace {
 
-TEST(CHERICapabilityFormat, Cheri64) {
-  const auto &Cheri64 = CHERICapabilityFormat::Cheri64();
+TEST(CHERICapabilityFormat, RV32Y) {
+  const auto &RV32Y = CHERICapabilityFormat::RV32Y();
 
-  EXPECT_EQ(Cheri64.getAddressMask(), 0xFFFFFFFF);
+  EXPECT_EQ(RV32Y.AddressMask, 0xFFFFFFFF);
 
-  // Lengths up to 63 are byte-aligned.
-  for (uint64_t Len = 1; Len <= 63; ++Len) {
-    EXPECT_EQ(Cheri64.getRepresentableLength(Len), Len);
-    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFFF);
+  // Lengths up to 4095 are byte-aligned.
+  for (uint64_t Len = 1; Len <= 4095; ++Len) {
+    EXPECT_EQ(RV32Y.getRepresentableLength(Len), Len);
+    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
-  // Lengths up to 120 are 8-byte-aligned.
-  for (uint64_t Len = 64; Len <= 120; ++Len) {
-    EXPECT_EQ(Cheri64.getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
-    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 8);
-    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFF8);
+  // Lengths up to 8191 are 8-byte-aligned.
+  for (uint64_t Len = 4096; Len <= 8191; ++Len) {
+    EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
+    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 8);
+    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF8);
   }
 
-  // Lengths up to 240 are 16-byte-aligned.
-  for (uint64_t Len = 121; Len <= 240; ++Len) {
-    EXPECT_EQ(Cheri64.getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
-    EXPECT_EQ(Cheri64.getRequiredAlignment(Len), 16);
-    EXPECT_EQ(Cheri64.getAlignmentMask(Len), 0xFFFFFFF0);
+  // Lengths up to 16383 are 16-byte-aligned.
+  for (uint64_t Len = 8192; Len <= 16383; ++Len) {
+    EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
+    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 16);
+    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF0);
   }
 }
 
-TEST(CHERICapabilityFormat, Cheri128) {
-  const auto &Cheri128 = CHERICapabilityFormat::Cheri128();
+TEST(CHERICapabilityFormat, RV64Y) {
+  const auto &RV64Y = CHERICapabilityFormat::RV64Y();
 
-  EXPECT_EQ(Cheri128.getAddressMask(), 0xFFFFFFFFFFFFFFFF);
+  EXPECT_EQ(RV64Y.AddressMask, 0xFFFFFFFFFFFFFFFF);
 
   // Lengths up to 4095 are byte-aligned.
   for (uint64_t Len = 1; Len <= 4095; ++Len) {
-    EXPECT_EQ(Cheri128.getRepresentableLength(Len), Len);
-    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFFF);
+    EXPECT_EQ(RV64Y.getRepresentableLength(Len), Len);
+    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFFF);
   }
 
-  // Lengths up to 8184 are 8-byte-aligned.
-  for (uint64_t Len = 4096; Len <= 8184; ++Len) {
-    EXPECT_EQ(Cheri128.getRepresentableLength(Len),
-              (Len + 7) & 0xFFFFFFFFFFFFFFF8);
-    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 8);
-    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF8);
+  // Lengths up to 8191 are 8-byte-aligned.
+  for (uint64_t Len = 4096; Len <= 8191; ++Len) {
+    assert(RV64Y.getRepresentableLength(Len) ==
+           ((Len + 7) & 0xFFFFFFFFFFFFFFF8));
+    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 8);
+    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF8);
   }
 
-  // Lengths up to 16368 are 16-byte-aligned.
-  for (uint64_t Len = 8185; Len <= 16368; ++Len) {
-    EXPECT_EQ(Cheri128.getRepresentableLength(Len),
+  // Lengths up to 16383 are 16-byte-aligned.
+  for (uint64_t Len = 8192; Len <= 16383; ++Len) {
+    EXPECT_EQ(RV64Y.getRepresentableLength(Len),
               (Len + 15) & 0xFFFFFFFFFFFFFFF0);
-    EXPECT_EQ(Cheri128.getRequiredAlignment(Len), 16);
-    EXPECT_EQ(Cheri128.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
+    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 16);
+    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
   }
 }
 
-TEST(CHERICapabilityFormat, Cheriot64) {
-  const auto &Cheriot64 = CHERICapabilityFormat::Cheriot64();
+TEST(CHERICapabilityFormat, CHERIoT) {
+  const auto &CHERIoT = CHERICapabilityFormat::CHERIoT();
 
-  EXPECT_EQ(Cheriot64.getAddressMask(), 0xFFFFFFFF);
+  EXPECT_EQ(CHERIoT.AddressMask, 0xFFFFFFFF);
 
   // Lengths up to 511 are byte-aligned.
   for (uint64_t Len = 1; Len <= 511; ++Len) {
-    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), Len);
-    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFF);
+    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), Len);
+    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 1);
+    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
   // Lengths up to 1022 are 2-byte-aligned.
   for (uint64_t Len = 512; Len <= 1022; ++Len) {
-    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), (Len + 1) & 0xFFFFFFFE);
-    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 2);
-    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFE);
+    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), (Len + 1) & 0xFFFFFFFE);
+    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 2);
+    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFE);
   }
 
   // Lengths up to 1022 are 4-byte-aligned.
   for (uint64_t Len = 1023; Len <= 2044; ++Len) {
-    EXPECT_EQ(Cheriot64.getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
-    EXPECT_EQ(Cheriot64.getRequiredAlignment(Len), 4);
-    EXPECT_EQ(Cheriot64.getAlignmentMask(Len), 0xFFFFFFFC);
+    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
+    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 4);
+    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFC);
   }
 }
 

>From f9b21f3e3929e4368890e121e60f3b67d48f7dac Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Tue, 2 Jun 2026 22:48:21 +0200
Subject: [PATCH 4/8] Fix mantissa size for RV32Y and update tests accordingly.

---
 llvm/lib/Support/CHERICapabilityFormat.cpp         |  2 +-
 .../Support/CHERICapabilityFormatTest.cpp          | 14 +++++++-------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index 9f66db8e2a102..89c8e07965128 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -53,7 +53,7 @@ class CHERIoTCapabilityFormatImpl : public CHERICapabilityFormatImpl<uint32_t> {
 };
 
 const CHERICapabilityFormatImpl<uint32_t> &RV32Y() {
-  static const RVYCapabilityFormatImpl<uint32_t, 14, 52> TheFormat;
+  static const RVYCapabilityFormatImpl<uint32_t, 10, 24> TheFormat;
   return TheFormat;
 }
 
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
index 249670519c966..aa630fabe68ff 100644
--- a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -10,22 +10,22 @@ TEST(CHERICapabilityFormat, RV32Y) {
 
   EXPECT_EQ(RV32Y.AddressMask, 0xFFFFFFFF);
 
-  // Lengths up to 4095 are byte-aligned.
-  for (uint64_t Len = 1; Len <= 4095; ++Len) {
+  // Lengths up to 255 are byte-aligned.
+  for (uint64_t Len = 1; Len <= 255; ++Len) {
     EXPECT_EQ(RV32Y.getRepresentableLength(Len), Len);
     EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 1);
     EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
-  // Lengths up to 8191 are 8-byte-aligned.
-  for (uint64_t Len = 4096; Len <= 8191; ++Len) {
+  // Lengths up to 511 are 8-byte-aligned.
+  for (uint64_t Len = 256; Len <= 511; ++Len) {
     EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
     EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 8);
     EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF8);
   }
 
-  // Lengths up to 16383 are 16-byte-aligned.
-  for (uint64_t Len = 8192; Len <= 16383; ++Len) {
+  // Lengths up to 1023 are 16-byte-aligned.
+  for (uint64_t Len = 512; Len <= 1023; ++Len) {
     EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
     EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 16);
     EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF0);
@@ -80,7 +80,7 @@ TEST(CHERICapabilityFormat, CHERIoT) {
     EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFE);
   }
 
-  // Lengths up to 1022 are 4-byte-aligned.
+  // Lengths up to 2044 are 4-byte-aligned.
   for (uint64_t Len = 1023; Len <= 2044; ++Len) {
     EXPECT_EQ(CHERIoT.getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
     EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 4);

>From 4143165e2d41cfdb44eab91279c17563ef44cd67 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Sat, 6 Jun 2026 00:18:33 +0200
Subject: [PATCH 5/8] Refactor to avoid need for virtual methods.

---
 .../llvm/Support/CHERICapabilityFormat.h      | 39 ++++----
 llvm/lib/Support/CHERICapabilityFormat.cpp    | 96 ++++++++++---------
 .../Support/CHERICapabilityFormatTest.cpp     | 66 ++++++-------
 3 files changed, 106 insertions(+), 95 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index 57eb1fef68bc0..66dc67b1c6046 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -14,11 +14,9 @@
 
 namespace llvm {
 
-namespace CHERICapabilityFormat {
-
-template <typename AddressType> class CHERICapabilityFormatImpl {
-public:
-  virtual ~CHERICapabilityFormatImpl() {}
+template <typename Derived, typename AddressType>
+struct CHERICapabilityFormatBase {
+  CHERICapabilityFormatBase() = delete;
 
   static constexpr uint64_t AddressMask =
       std::numeric_limits<AddressType>::max();
@@ -26,26 +24,33 @@ template <typename AddressType> class CHERICapabilityFormatImpl {
   /// Returns the "alignment mask" for an allocation of size \p Length. This
   /// mask is 0 where the capability format alignment requirements for the
   /// address to be 0, and 1 otherwise.
-  virtual uint64_t getAlignmentMask(uint64_t Length) const = 0;
+  static uint64_t getAlignmentMask(uint64_t Length);
 
   /// Returns the required alignment for an allocation of size \p Length.
-  Align getRequiredAlignment(uint64_t Length) const {
-    return Align((~getAlignmentMask(Length) + 1) & AddressMask);
-  }
+  static Align getRequiredAlignment(uint64_t Length);
 
   /// Returns \p Length rounded up to the nearest representable allocation
   /// length.
-  uint64_t getRepresentableLength(uint64_t Length) const {
-    uint64_t Mask = getAlignmentMask(Length);
-    return (Length + ~Mask) & Mask;
-  }
+  static uint64_t getRepresentableLength(uint64_t Length);
+};
+
+template <typename AddressType, unsigned MW, unsigned MAX_E>
+struct RVYCapabilityFormat
+    : public CHERICapabilityFormatBase<
+          RVYCapabilityFormat<AddressType, MW, MAX_E>, AddressType> {
+  RVYCapabilityFormat() = delete;
+  static uint64_t getAlignmentMask(uint64_t Length);
 };
 
-const CHERICapabilityFormatImpl<uint32_t> &RV32Y();
-const CHERICapabilityFormatImpl<uint64_t> &RV64Y();
-const CHERICapabilityFormatImpl<uint32_t> &CHERIoT();
+using RV32YCapabilityFormat = RVYCapabilityFormat<uint32_t, 10, 24>;
+using RV64YCapabilityFormat = RVYCapabilityFormat<uint64_t, 14, 24>;
+
+struct CHERIoTCapabilityFormat
+    : public CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t> {
+  CHERIoTCapabilityFormat() = delete;
+  static uint64_t getAlignmentMask(uint64_t Length);
+};
 
-} // namespace CHERICapabilityFormat
 } // namespace llvm
 
 #endif
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index 89c8e07965128..620cfbb7909a4 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -10,62 +10,68 @@
 #include "llvm/ADT/bit.h"
 
 namespace llvm {
-namespace CHERICapabilityFormat {
+
+template <typename Derived, typename AddressType>
+Align CHERICapabilityFormatBase<Derived, AddressType>::getRequiredAlignment(
+    uint64_t Length) {
+  return Align((~Derived::getAlignmentMask(Length) + 1) & AddressMask);
+}
+
+template <typename Derived, typename AddressType>
+uint64_t
+CHERICapabilityFormatBase<Derived, AddressType>::getRepresentableLength(
+    uint64_t Length) {
+  uint64_t Mask = Derived::getAlignmentMask(Length);
+  return (Length + ~Mask) & Mask;
+}
 
 template <typename AddressType, unsigned MW, unsigned MAX_E>
-class RVYCapabilityFormatImpl : public CHERICapabilityFormatImpl<AddressType> {
-  uint64_t getAlignmentMask(uint64_t Length) const override {
-    static constexpr unsigned int IE_TAKE_BITS = 3;
+uint64_t
+RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
+  static constexpr unsigned int IE_TAKE_BITS = 3;
 
-    if (Length == 0)
-      return ~static_cast<AddressType>(0) & this->AddressMask;
+  if (Length == 0)
+    return ~static_cast<AddressType>(0) & RVYCapabilityFormat::AddressMask;
 
-    // Extract bits that overflow the uncompressed mantissa window.
-    uint64_t Slice = static_cast<uint64_t>(Length) >> (MW - 1);
-    unsigned int E = 64 - llvm::countl_zero(Slice);
-    // We use internal exponent if length overflows OR the denormal boundary
-    // bit is set.
-    bool IE = (E != 0) || ((static_cast<uint64_t>(Length) >> (MW - 2)) & 1);
-    // Include bits use by the internal exponent for the shift value.
-    unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
+  // Extract bits that overflow the uncompressed mantissa window.
+  uint64_t Slice = static_cast<uint64_t>(Length) >> (MW - 1);
+  unsigned int E = 64 - llvm::countl_zero(Slice);
+  // We use internal exponent if length overflows OR the denormal boundary
+  // bit is set.
+  bool IE = (E != 0) || ((static_cast<uint64_t>(Length) >> (MW - 2)) & 1);
+  // Include bits use by the internal exponent for the shift value.
+  unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
 
-    // These are probably not needed, but were useful for debugging when I
-    // initially got it wrong.
-    assert(E <= MAX_E && "Raw exponent exceeds architecture maximum");
-    assert(Eprime <= sizeof(AddressType) * 8 &&
-           "Shift amount exceeds integer width");
+  // These are probably not needed, but were useful for debugging when I
+  // initially got it wrong.
+  assert(E <= MAX_E && "Raw exponent exceeds architecture maximum");
+  assert(Eprime <= sizeof(AddressType) * 8 &&
+         "Shift amount exceeds integer width");
 
-    // Left-shift ~0 to mask out the lost precision bits
-    return (~static_cast<AddressType>(0) << Eprime) & this->AddressMask;
-  }
-};
+  // Left-shift ~0 to mask out the lost precision bits
+  return (~static_cast<AddressType>(0) << Eprime) &
+         RVYCapabilityFormat::AddressMask;
+}
 
-class CHERIoTCapabilityFormatImpl : public CHERICapabilityFormatImpl<uint32_t> {
-  uint64_t getAlignmentMask(uint64_t Length) const override {
-    // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
-    for (unsigned e = 0; e <= 14; ++e) {
-      if (Length <= 511ULL << e)
-        return (0xFFFFFFFFFFFFFFFFULL << e) & this->AddressMask;
-    }
+template struct CHERICapabilityFormatBase<RVYCapabilityFormat<uint32_t, 10, 24>,
+                                          uint32_t>;
+template struct CHERICapabilityFormatBase<RVYCapabilityFormat<uint64_t, 14, 24>,
+                                          uint64_t>;
+template struct RVYCapabilityFormat<uint32_t, 10, 24>;
+template struct RVYCapabilityFormat<uint64_t, 14, 24>;
 
-    return (0xFFFFFFFFFFFFFFFFULL << 24) & this->AddressMask;
+uint64_t CHERIoTCapabilityFormat::getAlignmentMask(uint64_t Length) {
+  // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
+  for (unsigned e = 0; e <= 14; ++e) {
+    if (Length <= 511ULL << e)
+      return (~static_cast<uint32_t>(0) << e) &
+             CHERIoTCapabilityFormat::AddressMask;
   }
-};
-
-const CHERICapabilityFormatImpl<uint32_t> &RV32Y() {
-  static const RVYCapabilityFormatImpl<uint32_t, 10, 24> TheFormat;
-  return TheFormat;
-}
 
-const CHERICapabilityFormatImpl<uint64_t> &RV64Y() {
-  static const RVYCapabilityFormatImpl<uint64_t, 14, 24> TheFormat;
-  return TheFormat;
+  return (~static_cast<uint32_t>(0) << 24) &
+         CHERIoTCapabilityFormat::AddressMask;
 }
 
-const CHERICapabilityFormatImpl<uint32_t> &CHERIoT() {
-  static const CHERIoTCapabilityFormatImpl TheFormat;
-  return TheFormat;
-}
+template struct CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t>;
 
-} // namespace CHERICapabilityFormat
 } // namespace llvm
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
index aa630fabe68ff..9ddc5038996e6 100644
--- a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -6,85 +6,85 @@ using namespace llvm;
 namespace {
 
 TEST(CHERICapabilityFormat, RV32Y) {
-  const auto &RV32Y = CHERICapabilityFormat::RV32Y();
+  using RV32Y = RV32YCapabilityFormat;
 
-  EXPECT_EQ(RV32Y.AddressMask, 0xFFFFFFFF);
+  EXPECT_EQ(RV32Y::AddressMask, 0xFFFFFFFF);
 
   // Lengths up to 255 are byte-aligned.
   for (uint64_t Len = 1; Len <= 255; ++Len) {
-    EXPECT_EQ(RV32Y.getRepresentableLength(Len), Len);
-    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFFF);
+    EXPECT_EQ(RV32Y::getRepresentableLength(Len), Len);
+    EXPECT_EQ(RV32Y::getRequiredAlignment(Len), 1);
+    EXPECT_EQ(RV32Y::getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
   // Lengths up to 511 are 8-byte-aligned.
   for (uint64_t Len = 256; Len <= 511; ++Len) {
-    EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
-    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 8);
-    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF8);
+    EXPECT_EQ(RV32Y::getRepresentableLength(Len), (Len + 7) & 0xFFFFFFF8);
+    EXPECT_EQ(RV32Y::getRequiredAlignment(Len), 8);
+    EXPECT_EQ(RV32Y::getAlignmentMask(Len), 0xFFFFFFF8);
   }
 
   // Lengths up to 1023 are 16-byte-aligned.
   for (uint64_t Len = 512; Len <= 1023; ++Len) {
-    EXPECT_EQ(RV32Y.getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
-    EXPECT_EQ(RV32Y.getRequiredAlignment(Len), 16);
-    EXPECT_EQ(RV32Y.getAlignmentMask(Len), 0xFFFFFFF0);
+    EXPECT_EQ(RV32Y::getRepresentableLength(Len), (Len + 15) & 0xFFFFFFF0);
+    EXPECT_EQ(RV32Y::getRequiredAlignment(Len), 16);
+    EXPECT_EQ(RV32Y::getAlignmentMask(Len), 0xFFFFFFF0);
   }
 }
 
 TEST(CHERICapabilityFormat, RV64Y) {
-  const auto &RV64Y = CHERICapabilityFormat::RV64Y();
+  using RV64Y = RV64YCapabilityFormat;
 
-  EXPECT_EQ(RV64Y.AddressMask, 0xFFFFFFFFFFFFFFFF);
+  EXPECT_EQ(RV64Y::AddressMask, 0xFFFFFFFFFFFFFFFF);
 
   // Lengths up to 4095 are byte-aligned.
   for (uint64_t Len = 1; Len <= 4095; ++Len) {
-    EXPECT_EQ(RV64Y.getRepresentableLength(Len), Len);
-    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFFF);
+    EXPECT_EQ(RV64Y::getRepresentableLength(Len), Len);
+    EXPECT_EQ(RV64Y::getRequiredAlignment(Len), 1);
+    EXPECT_EQ(RV64Y::getAlignmentMask(Len), 0xFFFFFFFFFFFFFFFF);
   }
 
   // Lengths up to 8191 are 8-byte-aligned.
   for (uint64_t Len = 4096; Len <= 8191; ++Len) {
-    assert(RV64Y.getRepresentableLength(Len) ==
+    assert(RV64Y::getRepresentableLength(Len) ==
            ((Len + 7) & 0xFFFFFFFFFFFFFFF8));
-    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 8);
-    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF8);
+    EXPECT_EQ(RV64Y::getRequiredAlignment(Len), 8);
+    EXPECT_EQ(RV64Y::getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF8);
   }
 
   // Lengths up to 16383 are 16-byte-aligned.
   for (uint64_t Len = 8192; Len <= 16383; ++Len) {
-    EXPECT_EQ(RV64Y.getRepresentableLength(Len),
+    EXPECT_EQ(RV64Y::getRepresentableLength(Len),
               (Len + 15) & 0xFFFFFFFFFFFFFFF0);
-    EXPECT_EQ(RV64Y.getRequiredAlignment(Len), 16);
-    EXPECT_EQ(RV64Y.getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
+    EXPECT_EQ(RV64Y::getRequiredAlignment(Len), 16);
+    EXPECT_EQ(RV64Y::getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
   }
 }
 
 TEST(CHERICapabilityFormat, CHERIoT) {
-  const auto &CHERIoT = CHERICapabilityFormat::CHERIoT();
+  using CHERIoT = CHERIoTCapabilityFormat;
 
-  EXPECT_EQ(CHERIoT.AddressMask, 0xFFFFFFFF);
+  EXPECT_EQ(CHERIoT::AddressMask, 0xFFFFFFFF);
 
   // Lengths up to 511 are byte-aligned.
   for (uint64_t Len = 1; Len <= 511; ++Len) {
-    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), Len);
-    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 1);
-    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFF);
+    EXPECT_EQ(CHERIoT::getRepresentableLength(Len), Len);
+    EXPECT_EQ(CHERIoT::getRequiredAlignment(Len), 1);
+    EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
   // Lengths up to 1022 are 2-byte-aligned.
   for (uint64_t Len = 512; Len <= 1022; ++Len) {
-    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), (Len + 1) & 0xFFFFFFFE);
-    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 2);
-    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFE);
+    EXPECT_EQ(CHERIoT::getRepresentableLength(Len), (Len + 1) & 0xFFFFFFFE);
+    EXPECT_EQ(CHERIoT::getRequiredAlignment(Len), 2);
+    EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFE);
   }
 
   // Lengths up to 2044 are 4-byte-aligned.
   for (uint64_t Len = 1023; Len <= 2044; ++Len) {
-    EXPECT_EQ(CHERIoT.getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
-    EXPECT_EQ(CHERIoT.getRequiredAlignment(Len), 4);
-    EXPECT_EQ(CHERIoT.getAlignmentMask(Len), 0xFFFFFFFC);
+    EXPECT_EQ(CHERIoT::getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
+    EXPECT_EQ(CHERIoT::getRequiredAlignment(Len), 4);
+    EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFC);
   }
 }
 

>From ea647782f4a439a5deca7a141e1296d1ec06ccef Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Sat, 6 Jun 2026 22:14:22 +0200
Subject: [PATCH 6/8] Simplify by using AddressType for return and argument
 types Implement loop-free version of the CHERIoT logic.

---
 .../llvm/Support/CHERICapabilityFormat.h      | 13 ++++---
 llvm/lib/Support/CHERICapabilityFormat.cpp    | 36 ++++++++++---------
 .../Support/CHERICapabilityFormatTest.cpp     |  4 +--
 3 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index 66dc67b1c6046..94c0e73085c4a 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -18,20 +18,19 @@ template <typename Derived, typename AddressType>
 struct CHERICapabilityFormatBase {
   CHERICapabilityFormatBase() = delete;
 
-  static constexpr uint64_t AddressMask =
-      std::numeric_limits<AddressType>::max();
+  static constexpr AddressType AddressMask = ~static_cast<AddressType>(0);
 
   /// Returns the "alignment mask" for an allocation of size \p Length. This
   /// mask is 0 where the capability format alignment requirements for the
   /// address to be 0, and 1 otherwise.
-  static uint64_t getAlignmentMask(uint64_t Length);
+  static AddressType getAlignmentMask(AddressType Length);
 
   /// Returns the required alignment for an allocation of size \p Length.
-  static Align getRequiredAlignment(uint64_t Length);
+  static Align getRequiredAlignment(AddressType Length);
 
   /// Returns \p Length rounded up to the nearest representable allocation
   /// length.
-  static uint64_t getRepresentableLength(uint64_t Length);
+  static AddressType getRepresentableLength(AddressType Length);
 };
 
 template <typename AddressType, unsigned MW, unsigned MAX_E>
@@ -39,7 +38,7 @@ struct RVYCapabilityFormat
     : public CHERICapabilityFormatBase<
           RVYCapabilityFormat<AddressType, MW, MAX_E>, AddressType> {
   RVYCapabilityFormat() = delete;
-  static uint64_t getAlignmentMask(uint64_t Length);
+  static AddressType getAlignmentMask(uint64_t Length);
 };
 
 using RV32YCapabilityFormat = RVYCapabilityFormat<uint32_t, 10, 24>;
@@ -48,7 +47,7 @@ using RV64YCapabilityFormat = RVYCapabilityFormat<uint64_t, 14, 24>;
 struct CHERIoTCapabilityFormat
     : public CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t> {
   CHERIoTCapabilityFormat() = delete;
-  static uint64_t getAlignmentMask(uint64_t Length);
+  static uint32_t getAlignmentMask(uint32_t Length);
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index 620cfbb7909a4..35ae5ff66beb0 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -13,25 +13,25 @@ namespace llvm {
 
 template <typename Derived, typename AddressType>
 Align CHERICapabilityFormatBase<Derived, AddressType>::getRequiredAlignment(
-    uint64_t Length) {
+    AddressType Length) {
   return Align((~Derived::getAlignmentMask(Length) + 1) & AddressMask);
 }
 
 template <typename Derived, typename AddressType>
-uint64_t
+AddressType
 CHERICapabilityFormatBase<Derived, AddressType>::getRepresentableLength(
-    uint64_t Length) {
-  uint64_t Mask = Derived::getAlignmentMask(Length);
+    AddressType Length) {
+  AddressType Mask = Derived::getAlignmentMask(Length);
   return (Length + ~Mask) & Mask;
 }
 
 template <typename AddressType, unsigned MW, unsigned MAX_E>
-uint64_t
+AddressType
 RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
   static constexpr unsigned int IE_TAKE_BITS = 3;
 
   if (Length == 0)
-    return ~static_cast<AddressType>(0) & RVYCapabilityFormat::AddressMask;
+    return RVYCapabilityFormat::AddressMask;
 
   // Extract bits that overflow the uncompressed mantissa window.
   uint64_t Slice = static_cast<uint64_t>(Length) >> (MW - 1);
@@ -39,7 +39,7 @@ RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
   // We use internal exponent if length overflows OR the denormal boundary
   // bit is set.
   bool IE = (E != 0) || ((static_cast<uint64_t>(Length) >> (MW - 2)) & 1);
-  // Include bits use by the internal exponent for the shift value.
+  // Include bits used by the internal exponent for the shift value.
   unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
 
   // These are probably not needed, but were useful for debugging when I
@@ -49,8 +49,7 @@ RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
          "Shift amount exceeds integer width");
 
   // Left-shift ~0 to mask out the lost precision bits
-  return (~static_cast<AddressType>(0) << Eprime) &
-         RVYCapabilityFormat::AddressMask;
+  return RVYCapabilityFormat::AddressMask << Eprime;
 }
 
 template struct CHERICapabilityFormatBase<RVYCapabilityFormat<uint32_t, 10, 24>,
@@ -60,16 +59,19 @@ template struct CHERICapabilityFormatBase<RVYCapabilityFormat<uint64_t, 14, 24>,
 template struct RVYCapabilityFormat<uint32_t, 10, 24>;
 template struct RVYCapabilityFormat<uint64_t, 14, 24>;
 
-uint64_t CHERIoTCapabilityFormat::getAlignmentMask(uint64_t Length) {
+uint32_t CHERIoTCapabilityFormat::getAlignmentMask(uint32_t Length) {
   // Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
-  for (unsigned e = 0; e <= 14; ++e) {
-    if (Length <= 511ULL << e)
-      return (~static_cast<uint32_t>(0) << e) &
-             CHERIoTCapabilityFormat::AddressMask;
+  constexpr uint32_t NINE_SET_BITS = 511;
+  uint32_t E;
+  if (Length > NINE_SET_BITS << 14)
+    E = 24;
+  else {
+    E = Length > NINE_SET_BITS ? 32 - llvm::countl_zero(Length) - 9 : 0;
+    if (Length > NINE_SET_BITS << E)
+      ++E;
+    assert(E <= 14 && "CHERIoT capabilties cannot encode E between 14 and 24");
   }
-
-  return (~static_cast<uint32_t>(0) << 24) &
-         CHERIoTCapabilityFormat::AddressMask;
+  return CHERIoTCapabilityFormat::AddressMask << E;
 }
 
 template struct CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t>;
diff --git a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
index 9ddc5038996e6..1109bf9d19a40 100644
--- a/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
+++ b/llvm/unittests/Support/CHERICapabilityFormatTest.cpp
@@ -69,7 +69,7 @@ TEST(CHERICapabilityFormat, CHERIoT) {
   // Lengths up to 511 are byte-aligned.
   for (uint64_t Len = 1; Len <= 511; ++Len) {
     EXPECT_EQ(CHERIoT::getRepresentableLength(Len), Len);
-    EXPECT_EQ(CHERIoT::getRequiredAlignment(Len), 1);
+    assert(CHERIoT::getRequiredAlignment(Len) == 1);
     EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFF);
   }
 
@@ -83,7 +83,7 @@ TEST(CHERICapabilityFormat, CHERIoT) {
   // Lengths up to 2044 are 4-byte-aligned.
   for (uint64_t Len = 1023; Len <= 2044; ++Len) {
     EXPECT_EQ(CHERIoT::getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
-    EXPECT_EQ(CHERIoT::getRequiredAlignment(Len), 4);
+    assert(CHERIoT::getRequiredAlignment(Len) == 4);
     EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFC);
   }
 }

>From d4088181aa9df1b862aa379c8820cbc4f35bf11f Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Sun, 7 Jun 2026 21:58:30 +0200
Subject: [PATCH 7/8] Apply suggestions from code review

Co-authored-by: Alexander Richardson <mail at alexrichardson.me>
---
 llvm/include/llvm/Support/CHERICapabilityFormat.h | 2 +-
 llvm/lib/Support/CHERICapabilityFormat.cpp        | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index 94c0e73085c4a..250f48239c2a7 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -21,7 +21,7 @@ struct CHERICapabilityFormatBase {
   static constexpr AddressType AddressMask = ~static_cast<AddressType>(0);
 
   /// Returns the "alignment mask" for an allocation of size \p Length. This
-  /// mask is 0 where the capability format alignment requirements for the
+  /// mask is 0 where the capability format alignment requires the
   /// address to be 0, and 1 otherwise.
   static AddressType getAlignmentMask(AddressType Length);
 
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index 35ae5ff66beb0..ea24a7e9f20f1 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -69,7 +69,7 @@ uint32_t CHERIoTCapabilityFormat::getAlignmentMask(uint32_t Length) {
     E = Length > NINE_SET_BITS ? 32 - llvm::countl_zero(Length) - 9 : 0;
     if (Length > NINE_SET_BITS << E)
       ++E;
-    assert(E <= 14 && "CHERIoT capabilties cannot encode E between 14 and 24");
+    assert(E <= 14 && "CHERIoT capabilities cannot encode E between 14 and 24");
   }
   return CHERIoTCapabilityFormat::AddressMask << E;
 }

>From e7c89d9a022bd109e32c36f3e968d38d32a9c0d3 Mon Sep 17 00:00:00 2001
From: Owen Anderson <resistor at mac.com>
Date: Sun, 7 Jun 2026 22:03:59 +0200
Subject: [PATCH 8/8] Further suggested cleanups.

---
 llvm/include/llvm/Support/CHERICapabilityFormat.h | 2 --
 llvm/lib/Support/CHERICapabilityFormat.cpp        | 2 --
 2 files changed, 4 deletions(-)

diff --git a/llvm/include/llvm/Support/CHERICapabilityFormat.h b/llvm/include/llvm/Support/CHERICapabilityFormat.h
index 250f48239c2a7..51472944c9993 100644
--- a/llvm/include/llvm/Support/CHERICapabilityFormat.h
+++ b/llvm/include/llvm/Support/CHERICapabilityFormat.h
@@ -37,7 +37,6 @@ template <typename AddressType, unsigned MW, unsigned MAX_E>
 struct RVYCapabilityFormat
     : public CHERICapabilityFormatBase<
           RVYCapabilityFormat<AddressType, MW, MAX_E>, AddressType> {
-  RVYCapabilityFormat() = delete;
   static AddressType getAlignmentMask(uint64_t Length);
 };
 
@@ -46,7 +45,6 @@ using RV64YCapabilityFormat = RVYCapabilityFormat<uint64_t, 14, 24>;
 
 struct CHERIoTCapabilityFormat
     : public CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t> {
-  CHERIoTCapabilityFormat() = delete;
   static uint32_t getAlignmentMask(uint32_t Length);
 };
 
diff --git a/llvm/lib/Support/CHERICapabilityFormat.cpp b/llvm/lib/Support/CHERICapabilityFormat.cpp
index ea24a7e9f20f1..1429ea79d5411 100644
--- a/llvm/lib/Support/CHERICapabilityFormat.cpp
+++ b/llvm/lib/Support/CHERICapabilityFormat.cpp
@@ -42,8 +42,6 @@ RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
   // Include bits used by the internal exponent for the shift value.
   unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
 
-  // These are probably not needed, but were useful for debugging when I
-  // initially got it wrong.
   assert(E <= MAX_E && "Raw exponent exceeds architecture maximum");
   assert(Eprime <= sizeof(AddressType) * 8 &&
          "Shift amount exceeds integer width");



More information about the llvm-commits mailing list