[TrailingObjects] Dynamically realign under-aligned trailing objects.
authorJames Y Knight <jyknight@google.com>
Tue, 29 Dec 2015 04:00:43 +0000 (04:00 +0000)
committerJames Y Knight <jyknight@google.com>
Tue, 29 Dec 2015 04:00:43 +0000 (04:00 +0000)
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

llvm/include/llvm/Support/TrailingObjects.h
llvm/unittests/Support/TrailingObjectsTest.cpp

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