Skip to content

Commit

Permalink
Add a TrailingObjects template class.
Browse files Browse the repository at this point in the history
This is intended to help support the idiom of a class that has some
other objects (or multiple arrays of different types of objects)
appended on the end, which is used quite heavily in clang.

Differential Revision: http://reviews.llvm.org/D11272

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@244164 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
jyknight committed Aug 5, 2015
1 parent 3d08467 commit 1cf6cc7
Show file tree
Hide file tree
Showing 6 changed files with 412 additions and 26 deletions.
231 changes: 231 additions & 0 deletions include/llvm/Support/TrailingObjects.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
//===--- TrailingObjects.h - Variable-length classes ------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This header defines support for implementing classes that have
/// some trailing object (or arrays of objects) appended to them. The
/// main purpose is to make it obvious where this idiom is being used,
/// and to make the usage more idiomatic and more difficult to get
/// wrong.
///
/// 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.
///
/// Users are expected to derive from this template, and provide
/// numTrailingObjects implementations for each trailing type,
/// e.g. like this sample:
///
/// \code
/// class VarLengthObj : private TrailingObjects<VarLengthObj, int, double> {
/// friend TrailingObjects;
///
/// unsigned NumInts, NumDoubles;
/// size_t numTrailingObjects(OverloadToken<int>) const { return NumInts; }
/// size_t numTrailingObjects(OverloadToken<double>) const {
/// return NumDoubles;
/// }
/// };
/// \endcode
///
/// You can access the appended arrays via 'getTrailingObjects', and
/// determine the size needed for allocation via
/// 'additionalSizeToAlloc' and 'totalSizeToAlloc'.
///
/// All the methods implemented by this class are are intended for use
/// by the implementation of the class, not as part of its interface
/// (thus, private inheritance is suggested).
///
//===----------------------------------------------------------------------===//

#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/type_traits.h"

namespace llvm {

/// The base class for TrailingObjects* classes.
class TrailingObjectsBase {
protected:
/// OverloadToken's purpose is to allow specifying function overloads
/// for different types, without actually taking the types as
/// parameters. (Necessary because member function templates cannot
/// be specialized, so overloads must be used instead of
/// specialization.)
template <typename T> struct OverloadToken {};
};

// Internally used to indicate that the user didn't supply this value,
// so the explicit-specialization for fewer args will be used.
class NoTrailingTypeArg {};

// TODO: Consider using a single variadic implementation instead of
// multiple copies of the TrailingObjects template? [but, variadic
// template recursive implementations are annoying...]

/// This is the two-type version of the TrailingObjects template; see
/// file docstring for details.
template <typename BaseTy, typename TrailingTy1,
typename TrailingTy2 = NoTrailingTypeArg>
class TrailingObjects : public TrailingObjectsBase {
private:
// 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.
static void verifyTrailingObjectsAssertions() {
static_assert(llvm::AlignOf<BaseTy>::Alignment >=
llvm::AlignOf<TrailingTy1>::Alignment,
"TrailingTy1 requires more alignment than BaseTy provides");
static_assert(
llvm::AlignOf<TrailingTy1>::Alignment >=
llvm::AlignOf<TrailingTy2>::Alignment,
"TrailingTy2 requires more alignment than TrailingTy1 provides");

#ifdef LLVM_IS_FINAL
static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
#endif
}

// The next four functions are internal helpers for getTrailingObjects.
static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
OverloadToken<TrailingTy1>) {
return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
}

static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
OverloadToken<TrailingTy1>) {
return reinterpret_cast<TrailingTy1 *>(Obj + 1);
}

static const TrailingTy2 *getTrailingObjectsImpl(const BaseTy *Obj,
OverloadToken<TrailingTy2>) {
return reinterpret_cast<const TrailingTy2 *>(
getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
}

static TrailingTy2 *getTrailingObjectsImpl(BaseTy *Obj,
OverloadToken<TrailingTy2>) {
return reinterpret_cast<TrailingTy2 *>(
getTrailingObjectsImpl(Obj, OverloadToken<TrailingTy1>()) +
Obj->numTrailingObjects(OverloadToken<TrailingTy1>()));
}

protected:
/// Returns a pointer to the trailing object array of the given type
/// (which must be one of those specified in the class template). The
/// array may have zero or more elements in it.
template <typename T> const T *getTrailingObjects() const {
verifyTrailingObjectsAssertions();
// Forwards to an impl function with overloads, since member
// function templates can't be specialized.
return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
OverloadToken<T>());
}

/// Returns a pointer to the trailing object array of the given type
/// (which must be one of those specified in the class template). The
/// array may have zero or more elements in it.
template <typename T> T *getTrailingObjects() {
verifyTrailingObjectsAssertions();
// Forwards to an impl function with overloads, since member
// function templates can't be specialized.
return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
OverloadToken<T>());
}

/// Returns the size of the trailing data, if an object were
/// allocated with the given counts (The counts are in the same order
/// as the template arguments). This does not include the size of the
/// base object. The template arguments must be the same as those
/// used in the class; they are supplied here redundantly only so
/// that it's clear what the counts are counting in callers.
template <typename Ty1, typename Ty2,
typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value &&
std::is_same<Ty2, TrailingTy2>::value,
int>::type = 0>
static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1, size_t Count2) {
return sizeof(TrailingTy1) * Count1 + sizeof(TrailingTy2) * Count2;
}

/// Returns the total size of an object if it were allocated with the
/// given trailing object counts. This is the same as
/// additionalSizeToAlloc, except it *does* include the size of the base
/// object.
template <typename Ty1, typename Ty2>
static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1, size_t Count2) {
return sizeof(BaseTy) + additionalSizeToAlloc<Ty1, Ty2>(Count1, Count2);
}
};

/// This is the one-type version of the TrailingObjects template. See
/// the two-type version for more documentation.
template <typename BaseTy, typename TrailingTy1>
class TrailingObjects<BaseTy, TrailingTy1, NoTrailingTypeArg>
: public TrailingObjectsBase {
private:
static void verifyTrailingObjectsAssertions() {
static_assert(llvm::AlignOf<BaseTy>::Alignment >=
llvm::AlignOf<TrailingTy1>::Alignment,
"TrailingTy1 requires more alignment than BaseTy provides");

#ifdef LLVM_IS_FINAL
static_assert(LLVM_IS_FINAL(BaseTy), "BaseTy must be final.");
#endif
}

static const TrailingTy1 *getTrailingObjectsImpl(const BaseTy *Obj,
OverloadToken<TrailingTy1>) {
return reinterpret_cast<const TrailingTy1 *>(Obj + 1);
}

static TrailingTy1 *getTrailingObjectsImpl(BaseTy *Obj,
OverloadToken<TrailingTy1>) {
return reinterpret_cast<TrailingTy1 *>(Obj + 1);
}

protected:
template <typename T> const T *getTrailingObjects() const {
verifyTrailingObjectsAssertions();
return getTrailingObjectsImpl(static_cast<const BaseTy *>(this),
OverloadToken<T>());
}

template <typename T> T *getTrailingObjects() {
verifyTrailingObjectsAssertions();
return getTrailingObjectsImpl(static_cast<BaseTy *>(this),
OverloadToken<T>());
}

template <typename Ty1,
typename std::enable_if<std::is_same<Ty1, TrailingTy1>::value,
int>::type = 0>
static LLVM_CONSTEXPR size_t additionalSizeToAlloc(size_t Count1) {
return sizeof(TrailingTy1) * Count1;
}

template <typename Ty1>
static LLVM_CONSTEXPR size_t totalSizeToAlloc(size_t Count1) {
return sizeof(BaseTy) + additionalSizeToAlloc<Ty1>(Count1);
}
};

} // end namespace llvm

#endif
9 changes: 9 additions & 0 deletions include/llvm/Support/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ struct add_const_past_pointer<

}

// If the compiler supports detecting whether a class is final, define
// an LLVM_IS_FINAL macro. If it cannot be defined properly, this
// macro will be left undefined.
#if __cplusplus >= 201402L
#define LLVM_IS_FINAL(Ty) std::is_final<Ty>()
#elif __has_feature(is_final) || LLVM_GNUC_PREREQ(4, 7, 0)
#define LLVM_IS_FINAL(Ty) __is_final(Ty)
#endif

#ifdef LLVM_DEFINED_HAS_FEATURE
#undef __has_feature
#endif
Expand Down
37 changes: 19 additions & 18 deletions lib/IR/AttributeImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

#include "llvm/ADT/FoldingSet.h"
#include "llvm/IR/Attributes.h"
#include "llvm/Support/TrailingObjects.h"
#include <string>

namespace llvm {
Expand Down Expand Up @@ -141,13 +142,16 @@ class StringAttributeImpl : public AttributeImpl {
/// \class
/// \brief This class represents a group of attributes that apply to one
/// element: function, return type, or parameter.
class AttributeSetNode : public FoldingSetNode {
class AttributeSetNode final
: public FoldingSetNode,
private TrailingObjects<AttributeSetNode, Attribute> {
friend TrailingObjects;

unsigned NumAttrs; ///< Number of attributes in this node.

AttributeSetNode(ArrayRef<Attribute> Attrs) : NumAttrs(Attrs.size()) {
// There's memory after the node where we can store the entries in.
std::copy(Attrs.begin(), Attrs.end(),
reinterpret_cast<Attribute *>(this + 1));
std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects<Attribute>());
}

// AttributesSetNode is uniqued, these should not be publicly available.
Expand All @@ -170,7 +174,7 @@ class AttributeSetNode : public FoldingSetNode {
std::string getAsString(bool InAttrGrp) const;

typedef const Attribute *iterator;
iterator begin() const { return reinterpret_cast<iterator>(this + 1); }
iterator begin() const { return getTrailingObjects<Attribute>(); }
iterator end() const { return begin() + NumAttrs; }

void Profile(FoldingSetNodeID &ID) const {
Expand All @@ -181,27 +185,29 @@ class AttributeSetNode : public FoldingSetNode {
AttrList[I].Profile(ID);
}
};
static_assert(
AlignOf<AttributeSetNode>::Alignment >= AlignOf<Attribute>::Alignment,
"Alignment is insufficient for objects appended to AttributeSetNode");

//===----------------------------------------------------------------------===//
/// \class
/// \brief This class represents a set of attributes that apply to the function,
/// return type, and parameters.
class AttributeSetImpl : public FoldingSetNode {
friend class AttributeSet;
typedef std::pair<unsigned, AttributeSetNode *> IndexAttrPair;

public:
typedef std::pair<unsigned, AttributeSetNode*> IndexAttrPair;
class AttributeSetImpl final
: public FoldingSetNode,
private TrailingObjects<AttributeSetImpl, IndexAttrPair> {
friend class AttributeSet;
friend TrailingObjects;

private:
LLVMContext &Context;
unsigned NumAttrs; ///< Number of entries in this set.

// Helper fn for TrailingObjects class.
size_t numTrailingObjects(OverloadToken<IndexAttrPair>) { return NumAttrs; }

/// \brief Return a pointer to the IndexAttrPair for the specified slot.
const IndexAttrPair *getNode(unsigned Slot) const {
return reinterpret_cast<const IndexAttrPair *>(this + 1) + Slot;
return getTrailingObjects<IndexAttrPair>() + Slot;
}

// AttributesSet is uniqued, these should not be publicly available.
Expand All @@ -222,8 +228,7 @@ class AttributeSetImpl : public FoldingSetNode {
}
#endif
// There's memory after the node where we can store the entries in.
std::copy(Attrs.begin(), Attrs.end(),
reinterpret_cast<IndexAttrPair *>(this + 1));
std::copy(Attrs.begin(), Attrs.end(), getTrailingObjects<IndexAttrPair>());
}

/// \brief Get the context that created this AttributeSetImpl.
Expand Down Expand Up @@ -273,10 +278,6 @@ class AttributeSetImpl : public FoldingSetNode {

void dump() const;
};
static_assert(
AlignOf<AttributeSetImpl>::Alignment >=
AlignOf<AttributeSetImpl::IndexAttrPair>::Alignment,
"Alignment is insufficient for objects appended to AttributeSetImpl");

} // end llvm namespace

Expand Down
13 changes: 5 additions & 8 deletions lib/IR/Attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,7 @@ AttributeSetNode *AttributeSetNode::get(LLVMContext &C,
// new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeSetNode itself.
void *Mem = ::operator new(sizeof(AttributeSetNode) +
sizeof(Attribute) * SortedAttrs.size());
void *Mem = ::operator new(totalSizeToAlloc<Attribute>(SortedAttrs.size()));
PA = new (Mem) AttributeSetNode(SortedAttrs);
pImpl->AttrsSetNodes.InsertNode(PA, InsertPoint);
}
Expand Down Expand Up @@ -617,9 +616,8 @@ AttributeSet::getImpl(LLVMContext &C,
// create a new one and insert it.
if (!PA) {
// Coallocate entries after the AttributeSetImpl itself.
void *Mem = ::operator new(sizeof(AttributeSetImpl) +
sizeof(std::pair<unsigned, AttributeSetNode *>) *
Attrs.size());
void *Mem = ::operator new(
AttributeSetImpl::totalSizeToAlloc<IndexAttrPair>(Attrs.size()));
PA = new (Mem) AttributeSetImpl(C, Attrs);
pImpl->AttrsLists.InsertNode(PA, InsertPoint);
}
Expand Down Expand Up @@ -736,9 +734,8 @@ AttributeSet AttributeSet::get(LLVMContext &C, ArrayRef<AttributeSet> Attrs) {
if (!AS) continue;
SmallVector<std::pair<unsigned, AttributeSetNode *>, 8>::iterator
ANVI = AttrNodeVec.begin(), ANVE;
for (const AttributeSetImpl::IndexAttrPair
*AI = AS->getNode(0),
*AE = AS->getNode(AS->getNumAttributes());
for (const IndexAttrPair *AI = AS->getNode(0),
*AE = AS->getNode(AS->getNumAttributes());
AI != AE; ++AI) {
ANVE = AttrNodeVec.end();
while (ANVI != ANVE && ANVI->first <= AI->first)
Expand Down
1 change: 1 addition & 0 deletions unittests/Support/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ add_llvm_unittest(SupportTests
TargetRegistry.cpp
ThreadLocalTest.cpp
TimeValueTest.cpp
TrailingObjectsTest.cpp
UnicodeTest.cpp
YAMLIOTest.cpp
YAMLParserTest.cpp
Expand Down
Loading

0 comments on commit 1cf6cc7

Please sign in to comment.