1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 03:02:36 +01:00

[TrailingObjects] Dynamically realign under-aligned trailing objects.

Previously, the code enforced non-decreasing alignment of each trailing
type. However, it's easy enough to allow for realignment as needed, and
thus avoid the developer having to think about the possiblilities for
alignment requirements on all architectures.

(E.g. on Linux/x86, a struct with an int64 member is 4-byte aligned,
while on other 32-bit archs -- and even with other OSes on x86 -- it has
8-byte alignment. This sort of thing is irritating to have to manually
deal with.)

llvm-svn: 256533
This commit is contained in:
James Y Knight 2015-12-29 04:00:43 +00:00
parent 30e7ad8b6b
commit 37bdf9ea3e
2 changed files with 65 additions and 40 deletions

View File

@ -16,15 +16,14 @@
/// ///
/// The TrailingObject template abstracts away the reinterpret_cast, /// The TrailingObject template abstracts away the reinterpret_cast,
/// pointer arithmetic, and size calculations used for the allocation /// pointer arithmetic, and size calculations used for the allocation
/// and access of appended arrays of objects, as well as asserts that /// and access of appended arrays of objects, and takes care that they
/// the alignment of the classes involved are appropriate for the /// are all allocated at their required alignment. Additionally, it
/// usage. Additionally, it ensures that the base type is final -- /// ensures that the base type is final -- deriving from a class that
/// deriving from a class that expects data appended immediately after /// expects data appended immediately after it is typically not safe.
/// it is typically not safe.
/// ///
/// Users are expected to derive from this template, and provide /// Users are expected to derive from this template, and provide
/// numTrailingObjects implementations for each trailing type, /// numTrailingObjects implementations for each trailing type except
/// e.g. like this sample: /// the last, e.g. like this sample:
/// ///
/// \code /// \code
/// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> { /// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> {
@ -32,9 +31,6 @@
/// ///
/// unsigned NumInts, NumDoubles; /// unsigned NumInts, NumDoubles;
/// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; } /// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; }
/// size_t numTrailingObjects(OverloadToken<double>) const {
/// return NumDoubles;
/// }
/// }; /// };
/// \endcode /// \endcode
/// ///
@ -51,11 +47,12 @@
#ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H #ifndef LLVM_SUPPORT_TRAILINGOBJECTS_H
#define LLVM_SUPPORT_TRAILINGOBJECTS_H #define LLVM_SUPPORT_TRAILINGOBJECTS_H
#include <new>
#include <type_traits>
#include "llvm/Support/AlignOf.h" #include "llvm/Support/AlignOf.h"
#include "llvm/Support/Compiler.h" #include "llvm/Support/Compiler.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/type_traits.h" #include "llvm/Support/type_traits.h"
#include <new>
#include <type_traits>
namespace llvm { namespace llvm {
@ -149,13 +146,8 @@ struct TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy, NextTy,
using ParentType::getTrailingObjectsImpl; using ParentType::getTrailingObjectsImpl;
using ParentType::additionalSizeToAllocImpl; using ParentType::additionalSizeToAllocImpl;
static void verifyTrailingObjectsAssertions() { static LLVM_CONSTEXPR bool requiresRealignment() {
static_assert(llvm::AlignOf<PrevTy>::Alignment >= return llvm::AlignOf<PrevTy>::Alignment < llvm::AlignOf<NextTy>::Alignment;
llvm::AlignOf<NextTy>::Alignment,
"A trailing object requires more alignment than the previous "
"trailing object provides");
ParentType::verifyTrailingObjectsAssertions();
} }
// These two functions are helper functions for // These two functions are helper functions for
@ -170,30 +162,45 @@ struct TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy, NextTy,
static const NextTy * static const NextTy *
getTrailingObjectsImpl(const BaseTy *Obj, getTrailingObjectsImpl(const BaseTy *Obj,
TrailingObjectsBase::OverloadToken<NextTy>) { TrailingObjectsBase::OverloadToken<NextTy>) {
return reinterpret_cast<const NextTy *>( auto *Ptr = TopTrailingObj::getTrailingObjectsImpl(
TopTrailingObj::getTrailingObjectsImpl(
Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) +
TopTrailingObj::callNumTrailingObjects( TopTrailingObj::callNumTrailingObjects(
Obj, TrailingObjectsBase::OverloadToken<PrevTy>())); Obj, TrailingObjectsBase::OverloadToken<PrevTy>());
if (requiresRealignment())
return reinterpret_cast<const NextTy *>(
llvm::alignAddr(Ptr, llvm::alignOf<NextTy>()));
else
return reinterpret_cast<const NextTy *>(Ptr);
} }
static NextTy * static NextTy *
getTrailingObjectsImpl(BaseTy *Obj, getTrailingObjectsImpl(BaseTy *Obj,
TrailingObjectsBase::OverloadToken<NextTy>) { TrailingObjectsBase::OverloadToken<NextTy>) {
return reinterpret_cast<NextTy *>( auto *Ptr = TopTrailingObj::getTrailingObjectsImpl(
TopTrailingObj::getTrailingObjectsImpl(
Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) + Obj, TrailingObjectsBase::OverloadToken<PrevTy>()) +
TopTrailingObj::callNumTrailingObjects( TopTrailingObj::callNumTrailingObjects(
Obj, TrailingObjectsBase::OverloadToken<PrevTy>())); Obj, TrailingObjectsBase::OverloadToken<PrevTy>());
if (requiresRealignment())
return reinterpret_cast<NextTy *>(
llvm::alignAddr(Ptr, llvm::alignOf<NextTy>()));
else
return reinterpret_cast<NextTy *>(Ptr);
} }
// Helper function for TrailingObjects::additionalSizeToAlloc: this // Helper function for TrailingObjects::additionalSizeToAlloc: this
// function recurses to superclasses, each of which requires one // function recurses to superclasses, each of which requires one
// fewer size_t argument, and adds its own size. // fewer size_t argument, and adds its own size.
static LLVM_CONSTEXPR size_t additionalSizeToAllocImpl( static LLVM_CONSTEXPR size_t additionalSizeToAllocImpl(
size_t Count1, size_t SizeSoFar, size_t Count1,
typename ExtractSecondType<MoreTys, size_t>::type... MoreCounts) { typename ExtractSecondType<MoreTys, size_t>::type... MoreCounts) {
return sizeof(NextTy) * Count1 + additionalSizeToAllocImpl(MoreCounts...); return additionalSizeToAllocImpl(
(requiresRealignment()
? llvm::RoundUpToAlignment(SizeSoFar, llvm::alignOf<NextTy>())
: SizeSoFar) +
sizeof(NextTy) * Count1,
MoreCounts...);
} }
}; };
@ -207,9 +214,11 @@ struct TrailingObjectsImpl<Align, BaseTy, TopTrailingObj, PrevTy>
// up the inheritance chain to subclasses. // up the inheritance chain to subclasses.
static void getTrailingObjectsImpl(); static void getTrailingObjectsImpl();
static LLVM_CONSTEXPR size_t additionalSizeToAllocImpl() { return 0; } static LLVM_CONSTEXPR size_t additionalSizeToAllocImpl(size_t SizeSoFar) {
return SizeSoFar;
}
static void verifyTrailingObjectsAssertions() {} template <bool CheckAlignment> static void verifyTrailingObjectsAlignment() {}
}; };
} // end namespace trailing_objects_internal } // end namespace trailing_objects_internal
@ -238,15 +247,14 @@ class TrailingObjects : private trailing_objects_internal::TrailingObjectsImpl<
using ParentType::getTrailingObjectsImpl; using ParentType::getTrailingObjectsImpl;
// Contains static_assert statements for the alignment of the // This function contains only a static_assert BaseTy is final. The
// types. Must not be at class-level, because BaseTy isn't complete // static_assert must be in a function, and not at class-level
// at class instantiation time, but will be by the time this // because BaseTy isn't complete at class instantiation time, but
// function is instantiated. Recurses through the superclasses. // will be by the time this function is instantiated.
static void verifyTrailingObjectsAssertions() { static void verifyTrailingObjectsAssertions() {
#ifdef LLVM_IS_FINAL #ifdef LLVM_IS_FINAL
static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final."); static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
#endif #endif
ParentType::verifyTrailingObjectsAssertions();
} }
// These two methods are the base of the recursion for this method. // These two methods are the base of the recursion for this method.
@ -320,7 +328,7 @@ public:
additionalSizeToAlloc( additionalSizeToAlloc(
typename trailing_objects_internal::ExtractSecondType< typename trailing_objects_internal::ExtractSecondType<
TrailingTys, size_t>::type... Counts) { TrailingTys, size_t>::type... Counts) {
return ParentType::additionalSizeToAllocImpl(Counts...); return ParentType::additionalSizeToAllocImpl(0, Counts...);
} }
/// Returns the total size of an object if it were allocated with the /// Returns the total size of an object if it were allocated with the
@ -332,7 +340,7 @@ public:
std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t>::type std::is_same<Foo<TrailingTys...>, Foo<Tys...>>::value, size_t>::type
totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType< totalSizeToAlloc(typename trailing_objects_internal::ExtractSecondType<
TrailingTys, size_t>::type... Counts) { TrailingTys, size_t>::type... Counts) {
return sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(Counts...); return sizeof(BaseTy) + ParentType::additionalSizeToAllocImpl(0, Counts...);
} }
}; };

View File

@ -175,4 +175,21 @@ TEST(TrailingObjects, ThreeArg) {
reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1) + reinterpret_cast<short *>(reinterpret_cast<double *>(C + 1) + 1) +
1)); 1));
} }
class Class4 final : public TrailingObjects<Class4, char, long> {
friend TrailingObjects;
size_t numTrailingObjects(OverloadToken<char>) const { return 1; }
};
TEST(TrailingObjects, Realignment) {
EXPECT_EQ((Class4::additionalSizeToAlloc<char, long>(1, 1)),
llvm::RoundUpToAlignment(sizeof(long) + 1, llvm::alignOf<long>()));
EXPECT_EQ(sizeof(Class4), llvm::RoundUpToAlignment(1, llvm::alignOf<long>()));
std::unique_ptr<char[]> P(new char[1000]);
Class4 *C = reinterpret_cast<Class4 *>(P.get());
EXPECT_EQ(C->getTrailingObjects<char>(), reinterpret_cast<char *>(C + 1));
EXPECT_EQ(C->getTrailingObjects<long>(),
reinterpret_cast<long *>(llvm::alignAddr(
reinterpret_cast<char *>(C + 1) + 1, llvm::alignOf<long>())));
}
} }