[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:
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
|
||||
@@ -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
|
||||
@@ -21,6 +21,7 @@ add_llvm_unittest(SupportTests
|
||||
Caching.cpp
|
||||
Casting.cpp
|
||||
CheckedArithmeticTest.cpp
|
||||
CHERICapabilityFormatTest.cpp
|
||||
Chrono.cpp
|
||||
CommandLineTest.cpp
|
||||
CompressionTest.cpp
|
||||
|
||||
Reference in new issue
Block a user