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

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.

Co-authored-by: Alex Richardson <alexrichardson@google.com>
This commit is contained in:
Owen AndersonandAlex Richardson authored and GitHub committed 2026-06-15 15:02:37 +02:00
1 parent 9ce06fd298
commit 7dc09d0d3c
5 files changed
+223

No files matched your search

@@ -0,0 +1,53 @@
//===--- 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"
namespace llvm {
template <typename Derived, typename AddressType>
struct CHERICapabilityFormatBase {
CHERICapabilityFormatBase() = delete;
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 requires the
/// address to be 0, and 1 otherwise.
LLVM_ABI static AddressType getAlignmentMask(AddressType Length);
/// Returns the required alignment for an allocation of size \p Length.
LLVM_ABI static Align getRequiredAlignment(AddressType Length);
/// Returns \p Length rounded up to the nearest representable allocation
/// length.
LLVM_ABI static AddressType getRepresentableLength(AddressType Length);
};
template <typename AddressType, unsigned MW, unsigned MAX_E>
struct RVYCapabilityFormat
: public CHERICapabilityFormatBase<
RVYCapabilityFormat<AddressType, MW, MAX_E>, AddressType> {
LLVM_ABI static AddressType getAlignmentMask(uint64_t Length);
};
using RV32YCapabilityFormat = RVYCapabilityFormat<uint32_t, 10, 24>;
using RV64YCapabilityFormat = RVYCapabilityFormat<uint64_t, 14, 24>;
struct CHERIoTCapabilityFormat
: public CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t> {
LLVM_ABI static uint32_t getAlignmentMask(uint32_t Length);
};
} // namespace llvm
#endif
@@ -0,0 +1,77 @@
//===- 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"
#include "llvm/ADT/bit.h"
namespace llvm {
template <typename Derived, typename AddressType>
Align CHERICapabilityFormatBase<Derived, AddressType>::getRequiredAlignment(
AddressType Length) {
return Align((~Derived::getAlignmentMask(Length) + 1) & AddressMask);
}
template <typename Derived, typename AddressType>
AddressType
CHERICapabilityFormatBase<Derived, AddressType>::getRepresentableLength(
AddressType Length) {
AddressType Mask = Derived::getAlignmentMask(Length);
return (Length + ~Mask) & Mask;
}
template <typename AddressType, unsigned MW, unsigned MAX_E>
AddressType
RVYCapabilityFormat<AddressType, MW, MAX_E>::getAlignmentMask(uint64_t Length) {
static constexpr unsigned int IE_TAKE_BITS = 3;
if (Length == 0)
return 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 used by the internal exponent for the shift value.
unsigned int Eprime = IE ? (E + IE_TAKE_BITS) : 0;
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 RVYCapabilityFormat::AddressMask << Eprime;
}
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>;
uint32_t CHERIoTCapabilityFormat::getAlignmentMask(uint32_t Length) {
// Per section 7.13.4 and table 7.4 in the v1.0 CHERIoT specification.
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 capabilities cannot encode E between 14 and 24");
}
return CHERIoTCapabilityFormat::AddressMask << E;
}
template struct CHERICapabilityFormatBase<CHERIoTCapabilityFormat, uint32_t>;
} // namespace llvm
+1
View File
@@ -168,6 +168,7 @@ add_llvm_component_library(LLVMSupport
BuryPointer.cpp
CachePruning.cpp
Caching.cpp
CHERICapabilityFormat.cpp
circular_raw_ostream.cpp
Chrono.cpp
COM.cpp
@@ -0,0 +1,91 @@
#include "llvm/Support/CHERICapabilityFormat.h"
#include "gtest/gtest.h"
using namespace llvm;
namespace {
TEST(CHERICapabilityFormat, RV32Y) {
using RV32Y = RV32YCapabilityFormat;
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);
}
// 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 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);
}
}
TEST(CHERICapabilityFormat, RV64Y) {
using RV64Y = RV64YCapabilityFormat;
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);
}
// 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 16383 are 16-byte-aligned.
for (uint64_t Len = 8192; Len <= 16383; ++Len) {
EXPECT_EQ(RV64Y::getRepresentableLength(Len),
(Len + 15) & 0xFFFFFFFFFFFFFFF0);
EXPECT_EQ(RV64Y::getRequiredAlignment(Len), 16);
EXPECT_EQ(RV64Y::getAlignmentMask(Len), 0xFFFFFFFFFFFFFFF0);
}
}
TEST(CHERICapabilityFormat, CHERIoT) {
using CHERIoT = CHERIoTCapabilityFormat;
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);
assert(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);
}
// Lengths up to 2044 are 4-byte-aligned.
for (uint64_t Len = 1023; Len <= 2044; ++Len) {
EXPECT_EQ(CHERIoT::getRepresentableLength(Len), (Len + 3) & 0xFFFFFFFC);
assert(CHERIoT::getRequiredAlignment(Len) == 4);
EXPECT_EQ(CHERIoT::getAlignmentMask(Len), 0xFFFFFFFC);
}
}
} // namespace
+1
View File
@@ -21,6 +21,7 @@ add_llvm_unittest(SupportTests
Caching.cpp
Casting.cpp
CheckedArithmeticTest.cpp
CHERICapabilityFormatTest.cpp
Chrono.cpp
CommandLineTest.cpp
CompressionTest.cpp