Commit e83bb2fdfc2dc899526c8157fd8b77a68cdde9da

Fix Qt containers to properly support types with strict alignments.

QContiguousCache is a new type, so there are no binary compatibility
issues.

QHash and QMap didn't have any public qMalloc / qFree, so the entire
logic is contained in .cpp code. However, since old code will not
inform us of the alignment requirements, we need to add a bit to the
structure to indicate whether strict alignment is in use or not.

QList doesn't require any changes. For small, movable types, they're
all stored in the pointer array itself, so they're aligned. For larger
types, we use new(), so types with stricter requirements should define
their own operator new().

QLinkedList cannot be fixed. It uses new() on the QLinkedListNode,
which contains a T type. Sorry.

QVector did have public qMalloc / qFree. I've moved the calls to the
inner function and made it keep the old calls if the alignment
requirement is below a certain threshold. The idea is that, if it's
above, no one was using QVector anyway.

Reviewed-by: Bradley T. Hughes
  
144144 // faked-sizeof(void*) is properly aligned for a pointer
145145 faked.pptr[-1] = real.ptr;
146146
147 // and save the actual size just before the pointer
148 //reinterpret_cast<size_t *>(faked.pptr - 1)[-1] = size;
149
150147 return faked.ptr;
151148#endif
152149}
  
5656}
5757#endif
5858
59QContiguousCacheData *QContiguousCacheData::allocate(int size, int alignment)
60{
61 return static_cast<QContiguousCacheData *>(qMallocAligned(size, alignment));
62}
63
64void QContiguousCacheData::free(QContiguousCacheData *data)
65{
66 qFreeAligned(data);
67}
68
5969/*! \class QContiguousCache
6070 \brief The QContiguousCache class is a template class that provides a contiguous cache.
6171 \ingroup tools
  
6969 // there will be an 8 byte gap here if T requires 16-byte alignment
7070 // (such as long double on 64-bit platforms, __int128, __float128)
7171
72 static QContiguousCacheData *allocate(int size, int alignment);
73 static void free(QContiguousCacheData *data);
74
7275#ifdef QT_QCONTIGUOUSCACHE_DEBUG
7376 void dump() const;
7477#endif
164164 // count the padding at the end
165165 return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this);
166166 }
167 int alignOfTypedData() const
168 {
169#ifdef Q_ALIGNOF
170 return qMax<int>(sizeof(void*), Q_ALIGNOF(Data));
171#else
172 return 0;
173#endif
174 }
167175};
168176
169177template <typename T>
273273template <typename T>
274274inline QContiguousCacheData *QContiguousCache<T>::malloc(int aalloc)
275275{
276 return static_cast<QContiguousCacheData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T)));
276 return QContiguousCacheData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData());
277277}
278278
279279template <typename T>
328328 i = p->array;
329329 }
330330 }
331 qFree(x);
331 x->free(x);
332332}
333333template <typename T>
334334void QContiguousCache<T>::append(const T &value)
  
166166const int MinNumBits = 4;
167167
168168QHashData QHashData::shared_null = {
169 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true
169 0, 0, Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, MinNumBits, 0, 0, true, false
170170};
171171
172172void *QHashData::allocateNode()
173173{
174 void *ptr = qMalloc(nodeSize);
174 return allocateNode(0);
175}
176
177void *QHashData::allocateNode(int nodeAlign)
178{
179 void *ptr = strictAlignment ? qMallocAligned(nodeSize, nodeAlign) : qMalloc(nodeSize);
175180 Q_CHECK_PTR(ptr);
176181 return ptr;
177182}
178183
179184void QHashData::freeNode(void *node)
180185{
181 qFree(node);
186 if (strictAlignment)
187 qFreeAligned(node);
188 else
189 qFree(node);
182190}
183191
184192QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize)
185193{
186 return detach_helper( node_duplicate, 0, nodeSize );
194 return detach_helper2( node_duplicate, 0, nodeSize, 0 );
187195}
188196
189QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *),
190 void (*node_delete)(Node *),
191 int nodeSize)
197QHashData *QHashData::detach_helper2(void (*node_duplicate)(Node *, void *),
198 void (*node_delete)(Node *),
199 int nodeSize,
200 int nodeAlign)
192201{
193202 union {
194203 QHashData *d;
213213 d->numBits = numBits;
214214 d->numBuckets = numBuckets;
215215 d->sharable = true;
216 d->strictAlignment = nodeAlign > 8;
216217
217218 if (numBuckets) {
218219 QT_TRY {
232232 Node *oldNode = buckets[i];
233233 while (oldNode != this_e) {
234234 QT_TRY {
235 Node *dup = static_cast<Node *>(allocateNode());
235 Node *dup = static_cast<Node *>(allocateNode(nodeAlign));
236236
237237 QT_TRY {
238238 node_duplicate(oldNode, dup);
272272 while (cur != this_e) {
273273 Node *next = cur->next;
274274 node_delete(cur);
275 freeNode(cur);
275276 cur = next;
276277 }
277278 }
  
125125 short numBits;
126126 int numBuckets;
127127 uint sharable : 1;
128 uint strictAlignment : 1;
128129
129 void *allocateNode();
130 void *allocateNode(); // ### Qt5 remove me
131 void *allocateNode(int nodeAlign);
130132 void freeNode(void *node);
131133 QHashData *detach_helper(void (*node_duplicate)(Node *, void *), int nodeSize); // ### Qt5 remove me
132 QHashData *detach_helper(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *),
133 int nodeSize);
134 QHashData *detach_helper2(void (*node_duplicate)(Node *, void *), void (*node_delete)(Node *),
135 int nodeSize, int nodeAlign);
134136 void mightGrow();
135137 bool willGrow();
136138 void hasShrunk();
269269 return reinterpret_cast<Node *>(node);
270270 }
271271
272#ifdef Q_ALIGNOF
273 static inline int alignOfNode() { return qMax<int>(sizeof(void*), Q_ALIGNOF(Node)); }
274 static inline int alignOfDummyNode() { return qMax<int>(sizeof(void*), Q_ALIGNOF(DummyNode)); }
275#else
276 static inline int alignOfNode() { return 0; }
277 static inline int alignOfDummyNode() { return 0; }
278#endif
279
272280public:
273281 inline QHash() : d(&QHashData::shared_null) { d->ref.ref(); }
274282 inline QHash(const QHash<Key, T> &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); }
493493 Node **findNode(const Key &key, uint *hp = 0) const;
494494 Node *createNode(uint h, const Key &key, const T &value, Node **nextNode);
495495 void deleteNode(Node *node);
496 static void deleteNode(QHashData::Node *node);
496 static void deleteNode2(QHashData::Node *node);
497497
498498 static void duplicateNode(QHashData::Node *originalNode, void *newNode);
499499};
502502template <class Key, class T>
503503Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode(Node *node)
504504{
505 deleteNode(reinterpret_cast<QHashData::Node*>(node));
505 deleteNode2(reinterpret_cast<QHashData::Node*>(node));
506 d->freeNode(node);
506507}
507508
508
509509template <class Key, class T>
510Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode(QHashData::Node *node)
510Q_INLINE_TEMPLATE void QHash<Key, T>::deleteNode2(QHashData::Node *node)
511511{
512512#ifdef Q_CC_BOR
513513 concrete(node)->~QHashNode<Key, T>();
516516#else
517517 concrete(node)->~Node();
518518#endif
519 qFree(node);
520519}
521520
522521template <class Key, class T>
536536 Node *node;
537537
538538 if (QTypeInfo<T>::isDummy) {
539 node = reinterpret_cast<Node *>(new (d->allocateNode()) DummyNode(akey));
539 node = reinterpret_cast<Node *>(new (d->allocateNode(alignOfDummyNode())) DummyNode(akey));
540540 } else {
541 node = new (d->allocateNode()) Node(akey, avalue);
541 node = new (d->allocateNode(alignOfNode())) Node(akey, avalue);
542542 }
543543
544544 node->h = ah;
563563template <class Key, class T>
564564Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::freeData(QHashData *x)
565565{
566 x->free_helper(deleteNode);
566 x->free_helper(deleteNode2);
567567}
568568
569569template <class Key, class T>
575575template <class Key, class T>
576576Q_OUTOFLINE_TEMPLATE void QHash<Key, T>::detach_helper()
577577{
578 QHashData *x = d->detach_helper(duplicateNode, deleteNode,
579 QTypeInfo<T>::isDummy ? sizeof(DummyNode) : sizeof(Node));
578 QHashData *x = d->detach_helper2(duplicateNode, deleteNode2,
579 QTypeInfo<T>::isDummy ? sizeof(DummyNode) : sizeof(Node),
580 QTypeInfo<T>::isDummy ? alignOfDummyNode() : alignOfNode());
580581 if (!d->ref.deref())
581582 freeData(d);
582583 d = x;
  
5353QMapData QMapData::shared_null = {
5454 &shared_null,
5555 { &shared_null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
56 Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true
56 Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, 0, false, true, false
5757};
5858
5959QMapData *QMapData::createData()
6060{
61 return createData(0);
62}
63
64QMapData *QMapData::createData(int alignment)
65{
6166 QMapData *d = new QMapData;
6267 Q_CHECK_PTR(d);
6368 Node *e = reinterpret_cast<Node *>(d);
7474 d->randomBits = 0;
7575 d->insertInOrder = false;
7676 d->sharable = true;
77 d->strictAlignment = alignment > 8;
7778 return d;
7879}
7980
8686 while (cur != e) {
8787 prev = cur;
8888 cur = cur->forward[0];
89 qFree(reinterpret_cast<char *>(prev) - offset);
89 if (strictAlignment)
90 qFreeAligned(reinterpret_cast<char *>(prev) - offset);
91 else
92 qFree(reinterpret_cast<char *>(prev) - offset);
9093 }
9194 delete this;
9295}
9396
97QMapData::Node *QMapData::node_create(Node *update[], int offset)
98{
99 return node_create(update, offset, 0);
100}
101
94102/*!
95103 Creates a new node inside the data structure.
96104
108108 \a offset is an amount of bytes that needs to reserved just before the
109109 QMapData::Node structure.
110110
111 \a alignment dictates the alignment for the data.
112
111113 \internal
112114 \since 4.6
113115*/
114QMapData::Node *QMapData::node_create(Node *update[], int offset)
116QMapData::Node *QMapData::node_create(Node *update[], int offset, int alignment)
115117{
116118 int level = 0;
117119 uint mask = (1 << Sparseness) - 1;
134134 if (level == 3 && !insertInOrder)
135135 randomBits = qrand();
136136
137 void *concreteNode = qMalloc(offset + sizeof(Node) + level * sizeof(Node *));
137 void *concreteNode = strictAlignment ?
138 qMallocAligned(offset + sizeof(Node) + level * sizeof(Node *), alignment) :
139 qMalloc(offset + sizeof(Node) + level * sizeof(Node *));
138140 Q_CHECK_PTR(concreteNode);
139141
140142 Node *abstractNode = reinterpret_cast<Node *>(reinterpret_cast<char *>(concreteNode) + offset);
163163 update[i]->forward[i] = node->forward[i];
164164 }
165165 --size;
166 qFree(reinterpret_cast<char *>(node) - offset);
166 if (strictAlignment)
167 qFreeAligned(reinterpret_cast<char *>(node) - offset);
168 else
169 qFree(reinterpret_cast<char *>(node) - offset);
167170}
168171
169172#ifdef QT_QMAP_DEBUG
  
7474 uint randomBits;
7575 uint insertInOrder : 1;
7676 uint sharable : 1;
77 uint strictAlignment : 1;
7778
78 static QMapData *createData();
79 static QMapData *createData(); // ### Qt5 remove me
80 static QMapData *createData(int alignment);
7981 void continueFreeData(int offset);
80 Node *node_create(Node *update[], int offset);
82 Node *node_create(Node *update[], int offset); // ### Qt5 remove me
83 Node *node_create(Node *update[], int offset, int alignment);
8184 void node_delete(Node *update[], int offset, Node *node);
8285#ifdef QT_QMAP_DEBUG
8386 uint adjust_ptr(Node *node);
148148 };
149149
150150 static inline int payload() { return sizeof(PayloadNode) - sizeof(QMapData::Node *); }
151 static inline int alignment() {
152#ifdef Q_ALIGNOF
153 return qMax(sizeof(void*), Q_ALIGNOF(Node));
154#else
155 return 0;
156#endif
157 }
151158 static inline Node *concrete(QMapData::Node *node) {
152159 return reinterpret_cast<Node *>(reinterpret_cast<char *>(node) - payload());
153160 }
424424Q_INLINE_TEMPLATE typename QMapData::Node *
425425QMap<Key, T>::node_create(QMapData *adt, QMapData::Node *aupdate[], const Key &akey, const T &avalue)
426426{
427 QMapData::Node *abstractNode = adt->node_create(aupdate, payload());
427 QMapData::Node *abstractNode = adt->node_create(aupdate, payload(), alignment());
428428 QT_TRY {
429429 Node *concreteNode = concrete(abstractNode);
430430 new (&concreteNode->key) Key(akey);
725725Q_OUTOFLINE_TEMPLATE void QMap<Key, T>::detach_helper()
726726{
727727 union { QMapData *d; QMapData::Node *e; } x;
728 x.d = QMapData::createData();
728 x.d = QMapData::createData(alignment());
729729 if (d->size) {
730730 x.d->insertInOrder = true;
731731 QMapData::Node *update[QMapData::LastLevel + 1];
915915template <class Key, class T>
916916Q_OUTOFLINE_TEMPLATE QMap<Key, T>::QMap(const std::map<Key, T> &other)
917917{
918 d = QMapData::createData();
918 d = QMapData::createData(alignment());
919919 d->insertInOrder = true;
920920 typename std::map<Key,T>::const_iterator it = other.end();
921921 while (it != other.begin()) {
  
4545
4646QT_BEGIN_NAMESPACE
4747
48static inline int alignmentThreshold()
49{
50 // malloc on 32-bit platforms should return pointers that are 8-byte aligned or more
51 // while on 64-bit platforms they should be 16-byte aligned or more
52 return 2 * sizeof(void*);
53}
54
4855QVectorData QVectorData::shared_null = { Q_BASIC_ATOMIC_INITIALIZER(1), 0, 0, true, false };
4956
5057QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init)
6060 Q_CHECK_PTR(p);
6161 ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT);
6262 return p;
63}
64
65QVectorData *QVectorData::allocate(int size, int alignment)
66{
67 return static_cast<QVectorData *>(alignment > alignmentThreshold() ? qMallocAligned(size, alignment) : qMalloc(size));
68}
69
70QVectorData *QVectorData::reallocate(QVectorData *x, int newsize, int oldsize, int alignment)
71{
72 if (alignment > alignmentThreshold())
73 return static_cast<QVectorData *>(qReallocAligned(x, newsize, oldsize, alignment));
74 return static_cast<QVectorData *>(qRealloc(x, newsize));
75}
76
77void QVectorData::free(QVectorData *x, int alignment)
78{
79 if (alignment > alignmentThreshold())
80 qFreeAligned(x);
81 else
82 qFree(x);
6383}
6484
6585int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive)
  
7979 // some debugges when the QVector is member of a class within an unnamed namespace.
8080 // ### Qt 5: can be removed completely. (Ralf)
8181 static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init);
82 static QVectorData *allocate(int size, int alignment);
83 static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment);
84 static void free(QVectorData *data, int alignment);
8285 static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive);
8386};
8487
9090{ // private inheritance as we must not access QVectorData member thought QVectorTypedData
9191 // as this would break strict aliasing rules. (in the case of shared_null)
9292 T array[1];
93
94 static inline void free(QVectorTypedData *x, int alignment) { QVectorData::free(x, alignment); }
9395};
9496
9597class QRegion;
307307 // count the padding at the end
308308 return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this);
309309 }
310 inline int alignOfTypedData() const
311 {
312#ifdef Q_ALIGNOF
313 return qMax<int>(sizeof(void*), Q_ALIGNOF(Data));
314#else
315 return 0;
316#endif
317 }
310318};
311319
312320template <typename T>
386386template <typename T>
387387inline QVectorData *QVector<T>::malloc(int aalloc)
388388{
389 QVectorData *vectordata = static_cast<QVectorData *>(qMalloc(sizeOfTypedData() + (aalloc - 1) * sizeof(T)));
389 QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData());
390390 Q_CHECK_PTR(vectordata);
391391 return vectordata;
392392}
433433 while (i-- != b)
434434 i->~T();
435435 }
436 qFree(x);
436 x->free(x, alignOfTypedData());
437437}
438438
439439template <typename T>
472472 }
473473 } else {
474474 QT_TRY {
475 QVectorData *mem = static_cast<QVectorData *>(qRealloc(p, sizeOfTypedData() + (aalloc - 1) * sizeof(T)));
475 QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T),
476 sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData());
476477 Q_CHECK_PTR(mem);
477478 x.d = d = mem;
478479 x.d->size = d->size;
  
164164 void qtimerList();
165165 void containerTypedefs();
166166 void forwardDeclared();
167 void alignment();
167168};
168169
169170struct LargeStatic {
34813481 { typedef QQueue<T1> C; C *x = 0; C::iterator i; C::const_iterator j; Q_UNUSED(x) }
34823482 { typedef QSet<T1> C; C *x = 0; /* C::iterator i; */ C::const_iterator j; Q_UNUSED(x) }
34833483}
3484
3485#if defined(Q_ALIGNOF) && defined(Q_DECL_ALIGN)
3486
3487class Q_DECL_ALIGN(4) Aligned4
3488{
3489 char i;
3490public:
3491 Aligned4(int i = 0) : i(i) {}
3492 bool checkAligned() const
3493 {
3494 return (quintptr(this) & 3) == 0;
3495 }
3496
3497 inline bool operator==(const Aligned4 &other) const { return i == other.i; }
3498 inline bool operator<(const Aligned4 &other) const { return i < other.i; }
3499 friend inline int qHash(const Aligned4 &a) { return qHash(a.i); }
3500};
3501
3502class Q_DECL_ALIGN(128) Aligned128
3503{
3504 char i;
3505public:
3506 Aligned128(int i = 0) : i(i) {}
3507 bool checkAligned() const
3508 {
3509 return (quintptr(this) & 127) == 0;
3510 }
3511
3512 inline bool operator==(const Aligned128 &other) const { return i == other.i; }
3513 inline bool operator<(const Aligned128 &other) const { return i < other.i; }
3514 friend inline int qHash(const Aligned128 &a) { return qHash(a.i); }
3515};
3516
3517template<typename C>
3518void testVectorAlignment()
3519{
3520 typedef typename C::value_type Aligned;
3521 C container;
3522 container.append(Aligned());
3523 QVERIFY(container[0].checkAligned());
3524
3525 for (int i = 0; i < 200; ++i)
3526 container.append(Aligned());
3527
3528 for (int i = 0; i < container.size(); ++i)
3529 QVERIFY(container.at(i).checkAligned());
3530}
3531
3532template<typename C>
3533void testContiguousCacheAlignment()
3534{
3535 typedef typename C::value_type Aligned;
3536 C container(150);
3537 container.append(Aligned());
3538 QVERIFY(container[container.firstIndex()].checkAligned());
3539
3540 for (int i = 0; i < 200; ++i)
3541 container.append(Aligned());
3542
3543 for (int i = container.firstIndex(); i < container.lastIndex(); ++i)
3544 QVERIFY(container.at(i).checkAligned());
3545}
3546
3547template<typename C>
3548void testAssociativeContainerAlignment()
3549{
3550 typedef typename C::key_type Key;
3551 typedef typename C::mapped_type Value;
3552 C container;
3553 container.insert(Key(), Value());
3554
3555 typename C::const_iterator it = container.constBegin();
3556 QVERIFY(it.key().checkAligned());
3557 QVERIFY(it.value().checkAligned());
3558
3559 // add some more elements
3560 for (int i = 0; i < 200; ++i)
3561 container.insert(Key(i), Value(i));
3562
3563 it = container.constBegin();
3564 for ( ; it != container.constEnd(); ++it) {
3565 QVERIFY(it.key().checkAligned());
3566 QVERIFY(it.value().checkAligned());
3567 }
3568}
3569
3570void tst_Collections::alignment()
3571{
3572 testVectorAlignment<QVector<Aligned4> >();
3573 testVectorAlignment<QVector<Aligned128> >();
3574 testContiguousCacheAlignment<QContiguousCache<Aligned4> >();
3575 testContiguousCacheAlignment<QContiguousCache<Aligned128> >();
3576 testAssociativeContainerAlignment<QMap<Aligned4, Aligned4> >();
3577 testAssociativeContainerAlignment<QMap<Aligned4, Aligned128> >();
3578 testAssociativeContainerAlignment<QMap<Aligned128, Aligned4> >();
3579 testAssociativeContainerAlignment<QMap<Aligned128, Aligned128> >();
3580 testAssociativeContainerAlignment<QHash<Aligned4, Aligned4> >();
3581 testAssociativeContainerAlignment<QHash<Aligned4, Aligned128> >();
3582 testAssociativeContainerAlignment<QHash<Aligned128, Aligned4> >();
3583 testAssociativeContainerAlignment<QHash<Aligned128, Aligned128> >();
3584}
3585
3586#else
3587void tst_Collections::alignment()
3588{
3589 QSKIP("Compiler doesn't support necessary extension keywords", SkipAll)
3590}
3591#endif
34843592
34853593QTEST_APPLESS_MAIN(tst_Collections)
34863594#include "tst_collections.moc"