Issue #2148 - Make Vector not use AlignedStorage for its inline element storage

See Bug 1338374 1/2
This commit is contained in:
FranklinDM 2023-03-09 14:45:24 +08:00 • committed by roytam1
commit 0365f940fe
5 changed files with 108 additions and 48 deletions

View file

@ -46,7 +46,8 @@ struct LoopIterationBound : public TempObject
// of the loop header. This will use loop invariant terms and header phis. // of the loop header. This will use loop invariant terms and header phis.
LinearSum currentSum; LinearSum currentSum;
LoopIterationBound(MBasicBlock* header, MTest* test, LinearSum boundSum, LinearSum currentSum) LoopIterationBound(MBasicBlock* header, MTest* test,
const LinearSum& boundSum, const LinearSum& currentSum)
: header(header), test(test), : header(header), test(test),
boundSum(boundSum), currentSum(currentSum) boundSum(boundSum), currentSum(currentSum)
{ {
@ -59,7 +60,7 @@ typedef Vector<LoopIterationBound*, 0, SystemAllocPolicy> LoopIterationBoundVect
struct SymbolicBound : public TempObject struct SymbolicBound : public TempObject
{ {
private: private:
SymbolicBound(LoopIterationBound* loop, LinearSum sum) SymbolicBound(LoopIterationBound* loop, const LinearSum& sum)
: loop(loop), sum(sum) : loop(loop), sum(sum)
{ {
} }
@ -73,7 +74,8 @@ struct SymbolicBound : public TempObject
// If nullptr, then 'sum' is always valid. // If nullptr, then 'sum' is always valid.
LoopIterationBound* loop; LoopIterationBound* loop;
static SymbolicBound* New(TempAllocator& alloc, LoopIterationBound* loop, LinearSum sum) { static SymbolicBound*
New(TempAllocator& alloc, LoopIterationBound* loop, const LinearSum& sum) {
return new(alloc) SymbolicBound(loop, sum); return new(alloc) SymbolicBound(loop, sum);
} }

View file

@ -23,6 +23,7 @@
/* Note: Aborts on OOM. */ /* Note: Aborts on OOM. */
class JSAPITestString { class JSAPITestString {
js::Vector<char, 0, js::SystemAllocPolicy> chars; js::Vector<char, 0, js::SystemAllocPolicy> chars;
public: public:
JSAPITestString() {} JSAPITestString() {}
explicit JSAPITestString(const char* s) { *this += s; } explicit JSAPITestString(const char* s) { *this += s; }
@ -32,21 +33,34 @@ class JSAPITestString {
const char* end() const { return chars.end(); } const char* end() const { return chars.end(); }
size_t length() const { return chars.length(); } size_t length() const { return chars.length(); }
JSAPITestString & operator +=(const char* s) { JSAPITestString& operator +=(const char* s) {
if (!chars.append(s, strlen(s))) if (!chars.append(s, strlen(s)))
abort(); abort();
return *this; return *this;
} }
JSAPITestString & operator +=(const JSAPITestString& s) { JSAPITestString& operator +=(const JSAPITestString& s) {
if (!chars.append(s.begin(), s.length())) if (!chars.append(s.begin(), s.length()))
abort(); abort();
return *this; return *this;
} }
}; };
inline JSAPITestString operator+(JSAPITestString a, const char* b) { return a += b; } inline JSAPITestString
inline JSAPITestString operator+(JSAPITestString a, const JSAPITestString& b) { return a += b; } operator+(const JSAPITestString& a, const char* b)
{
JSAPITestString result = a;
result += b;
return result;
}
inline JSAPITestString
operator+(const JSAPITestString& a, const JSAPITestString& b)
{
JSAPITestString result = a;
result += b;
return result;
}
class JSAPITest class JSAPITest
{ {
@ -205,7 +219,11 @@ class JSAPITest
return fail(JSAPITestString("CHECK failed: " #expr), __FILE__, __LINE__); \ return fail(JSAPITestString("CHECK failed: " #expr), __FILE__, __LINE__); \
} while (false) } while (false)
bool fail(JSAPITestString msg = JSAPITestString(), const char* filename = "-", int lineno = 0) { bool fail(const JSAPITestString& msg = JSAPITestString(),
const char* filename = "-",
int lineno = 0)
{
JSAPITestString message = msg;
if (JS_IsExceptionPending(cx)) { if (JS_IsExceptionPending(cx)) {
js::gc::AutoSuppressGC gcoff(cx); js::gc::AutoSuppressGC gcoff(cx);
JS::RootedValue v(cx); JS::RootedValue v(cx);
@ -215,11 +233,12 @@ class JSAPITest
if (s) { if (s) {
JSAutoByteString bytes(cx, s); JSAutoByteString bytes(cx, s);
if (!!bytes) if (!!bytes)
msg += bytes.ptr(); message += bytes.ptr();
} }
} }
fprintf(stderr, "%s:%d:%.*s\n", filename, lineno, (int) msg.length(), msg.begin()); fprintf(stderr, "%s:%d:%.*s\n",
msgs += msg; filename, lineno, int(message.length()), message.begin());
msgs += message;
return false; return false;
} }

View file

@ -277,7 +277,7 @@ struct VectorTesting;
template<typename T, template<typename T,
size_t MinInlineCapacity = 0, size_t MinInlineCapacity = 0,
class AllocPolicy = MallocAllocPolicy> class AllocPolicy = MallocAllocPolicy>
class Vector final : private AllocPolicy class MOZ_NON_PARAM Vector final : private AllocPolicy
{ {
/* utilities */ /* utilities */
@ -293,36 +293,39 @@ class Vector final : private AllocPolicy
/* magic constants */ /* magic constants */
static const int kMaxInlineBytes = 1024; /**
* The maximum space allocated for inline element storage.
/* compute constants */
/*
* Consider element size to be 1 for buffer sizing if there are 0 inline
* elements. This allows us to compile when the definition of the element
* type is not visible here.
* *
* Explicit specialization is only allowed at namespace scope, so in order * We reduce space by what the AllocPolicy base class and prior Vector member
* to keep everything here, we use a dummy template parameter with partial * fields likely consume to attempt to play well with binary size classes.
* specialization.
*/ */
template<int M, int Dummy> static constexpr size_t kMaxInlineBytes =
struct ElemSize 1024 -
{ (sizeof(AllocPolicy) + sizeof(T*) + sizeof(size_t) + sizeof(size_t));
static const size_t value = sizeof(T);
}; /**
template<int Dummy> * The number of T elements of inline capacity built into this Vector. This
struct ElemSize<0, Dummy> * is usually |MinInlineCapacity|, but it may be less (or zero!) for large T.
{ *
static const size_t value = 1; * We use a partially-specialized template (not explicit specialization, which
* is only allowed at namespace scope) to compute this value. The benefit is
* that |sizeof(T)| need not be computed, and |T| doesn't have to be fully
* defined at the time |Vector<T>| appears, if no inline storage is requested.
*/
template <size_t MinimumInlineCapacity, size_t Dummy>
struct ComputeCapacity {
static constexpr size_t value =
tl::Min<MinimumInlineCapacity, kMaxInlineBytes / sizeof(T)>::value;
}; };
static const size_t kInlineCapacity = template <size_t Dummy>
tl::Min<MinInlineCapacity, kMaxInlineBytes / ElemSize<MinInlineCapacity, 0>::value>::value; struct ComputeCapacity<0, Dummy> {
static constexpr size_t value = 0;
};
/* Calculate inline buffer size; avoid 0-sized array. */ /** The actual inline capacity in number of elements T. This may be zero! */
static const size_t kInlineBytes = static constexpr size_t kInlineCapacity =
tl::Max<1, kInlineCapacity * ElemSize<MinInlineCapacity, 0>::value>::value; ComputeCapacity<MinInlineCapacity, 0>::value;
/* member data */ /* member data */
@ -346,8 +349,34 @@ class Vector final : private AllocPolicy
size_t mReserved; size_t mReserved;
#endif #endif
/* Memory used for inline storage. */ /*
AlignedStorage<kInlineBytes> mStorage; * Memory used for inline storage. We want basically this:
*
* alignas(T) unsigned char storage[kInlineCapacity * sizeof(T)];
*
* but C++ forbids zero-sized arrays that might result if we did this. We fix
* this by (again) using partial specialization, defining an array only if
* contains at least one element.
*/
template<size_t Capacity, size_t Dummy>
struct InlineStorage
{
alignas(T) unsigned char mBytes[Capacity * sizeof(T)];
// GCC fails due to -Werror=strict-aliasing if |mBytes| is directly cast to
// T*. Indirecting through this function addresses the problem.
void* data() { return mBytes; }
T* addr() { return static_cast<T*>(data()); }
};
template<size_t Dummy>
struct InlineStorage<0, Dummy>
{
T* addr() { return nullptr; }
};
InlineStorage<kInlineCapacity, 0> mStorage;
#ifdef DEBUG #ifdef DEBUG
friend class ReentrancyGuard; friend class ReentrancyGuard;
@ -363,7 +392,7 @@ class Vector final : private AllocPolicy
T* inlineStorage() T* inlineStorage()
{ {
return static_cast<T*>(mStorage.addr()); return mStorage.addr();
} }
T* beginNoCheck() const T* beginNoCheck() const
@ -771,7 +800,7 @@ Vector<T, N, AP>::Vector(AP aAP)
, mEntered(false) , mEntered(false)
#endif #endif
{ {
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
} }
/* Move constructor. */ /* Move constructor. */
@ -791,7 +820,7 @@ Vector<T, N, AllocPolicy>::Vector(Vector&& aRhs)
if (aRhs.usingInlineStorage()) { if (aRhs.usingInlineStorage()) {
/* We can't move the buffer over in this case, so copy elements. */ /* We can't move the buffer over in this case, so copy elements. */
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
Impl::moveConstruct(mBegin, aRhs.beginNoCheck(), aRhs.endNoCheck()); Impl::moveConstruct(mBegin, aRhs.beginNoCheck(), aRhs.endNoCheck());
/* /*
* Leave aRhs's mLength, mBegin, mCapacity, and mReserved as they are. * Leave aRhs's mLength, mBegin, mCapacity, and mReserved as they are.
@ -803,7 +832,7 @@ Vector<T, N, AllocPolicy>::Vector(Vector&& aRhs)
* in-line storage. * in-line storage.
*/ */
mBegin = aRhs.mBegin; mBegin = aRhs.mBegin;
aRhs.mBegin = static_cast<T*>(aRhs.mStorage.addr()); aRhs.mBegin = aRhs.inlineStorage();
aRhs.mCapacity = kInlineCapacity; aRhs.mCapacity = kInlineCapacity;
aRhs.mLength = 0; aRhs.mLength = 0;
#ifdef DEBUG #ifdef DEBUG
@ -1142,7 +1171,7 @@ Vector<T, N, AP>::clearAndFree()
return; return;
} }
this->free_(beginNoCheck()); this->free_(beginNoCheck());
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
mCapacity = kInlineCapacity; mCapacity = kInlineCapacity;
#ifdef DEBUG #ifdef DEBUG
mReserved = 0; mReserved = 0;
@ -1370,7 +1399,7 @@ Vector<T, N, AP>::extractRawBuffer()
} }
T* ret = mBegin; T* ret = mBegin;
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
mLength = 0; mLength = 0;
mCapacity = kInlineCapacity; mCapacity = kInlineCapacity;
#ifdef DEBUG #ifdef DEBUG
@ -1396,7 +1425,7 @@ Vector<T, N, AP>::extractOrCopyRawBuffer()
Impl::moveConstruct(copy, beginNoCheck(), endNoCheck()); Impl::moveConstruct(copy, beginNoCheck(), endNoCheck());
Impl::destroy(beginNoCheck(), endNoCheck()); Impl::destroy(beginNoCheck(), endNoCheck());
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
mLength = 0; mLength = 0;
mCapacity = kInlineCapacity; mCapacity = kInlineCapacity;
#ifdef DEBUG #ifdef DEBUG
@ -1424,7 +1453,7 @@ Vector<T, N, AP>::replaceRawBuffer(T* aP, size_t aLength)
* otherwise be acceptable. Maybe this behaviour should be * otherwise be acceptable. Maybe this behaviour should be
* specifiable with an argument to this function. * specifiable with an argument to this function.
*/ */
mBegin = static_cast<T*>(mStorage.addr()); mBegin = inlineStorage();
mLength = aLength; mLength = aLength;
mCapacity = kInlineCapacity; mCapacity = kInlineCapacity;
Impl::moveConstruct(mBegin, aP, aP + aLength); Impl::moveConstruct(mBegin, aP, aP + aLength);

View file

@ -172,10 +172,17 @@ CTLogVerifier::Verify(const LogEntry& entry,
if (rv != Success) { if (rv != Success) {
return rv; return rv;
} }
// sct.extensions may be empty. If it is, sctExtensionsInput will remain in
// its default state, which is valid but of length 0.
Input sctExtensionsInput; Input sctExtensionsInput;
rv = BufferToInput(sct.extensions, sctExtensionsInput); rv = BufferToInput(sct.extensions, sctExtensionsInput);
if (rv != Success) { if (sct.extensions.length() > 0) {
return rv; rv = sctExtensionsInput.Init(sct.extensions.begin(),
sct.extensions.length());
if (rv != Success) {
return rv;
}
} }
Buffer serializedData; Buffer serializedData;

View file

@ -115,6 +115,9 @@ struct SignedCertificateTimestamp
inline pkix::Result BufferToInput(const Buffer& buffer, pkix::Input& input) inline pkix::Result BufferToInput(const Buffer& buffer, pkix::Input& input)
{ {
if (buffer.length() == 0) {
return pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
}
return input.Init(buffer.begin(), buffer.length()); return input.Init(buffer.begin(), buffer.length());
} }