1*da0073e9SAndroid Build Coastguard Worker //===- llvm/ADT/SmallVector.cpp - 'Normally small' vectors ----------------===//
2*da0073e9SAndroid Build Coastguard Worker //
3*da0073e9SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*da0073e9SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*da0073e9SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*da0073e9SAndroid Build Coastguard Worker //
7*da0073e9SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*da0073e9SAndroid Build Coastguard Worker //
9*da0073e9SAndroid Build Coastguard Worker // This file implements the SmallVector class.
10*da0073e9SAndroid Build Coastguard Worker //
11*da0073e9SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
12*da0073e9SAndroid Build Coastguard Worker
13*da0073e9SAndroid Build Coastguard Worker // ATen: modified from llvm::SmallVector.
14*da0073e9SAndroid Build Coastguard Worker // replaced llvm::safe_malloc with std::bad_alloc
15*da0073e9SAndroid Build Coastguard Worker // deleted LLVM_ENABLE_EXCEPTIONS
16*da0073e9SAndroid Build Coastguard Worker
17*da0073e9SAndroid Build Coastguard Worker #include <c10/util/SmallVector.h>
18*da0073e9SAndroid Build Coastguard Worker #include <cstdint>
19*da0073e9SAndroid Build Coastguard Worker #include <stdexcept>
20*da0073e9SAndroid Build Coastguard Worker #include <string>
21*da0073e9SAndroid Build Coastguard Worker using namespace c10;
22*da0073e9SAndroid Build Coastguard Worker
23*da0073e9SAndroid Build Coastguard Worker // Check that no bytes are wasted and everything is well-aligned.
24*da0073e9SAndroid Build Coastguard Worker namespace {
25*da0073e9SAndroid Build Coastguard Worker // These structures may cause binary compat warnings on AIX. Suppress the
26*da0073e9SAndroid Build Coastguard Worker // warning since we are only using these types for the static assertions below.
27*da0073e9SAndroid Build Coastguard Worker #if defined(_AIX)
28*da0073e9SAndroid Build Coastguard Worker #pragma GCC diagnostic push
29*da0073e9SAndroid Build Coastguard Worker #pragma GCC diagnostic ignored "-Waix-compat"
30*da0073e9SAndroid Build Coastguard Worker #endif
31*da0073e9SAndroid Build Coastguard Worker struct Struct16B {
32*da0073e9SAndroid Build Coastguard Worker alignas(16) void* X;
33*da0073e9SAndroid Build Coastguard Worker };
34*da0073e9SAndroid Build Coastguard Worker struct Struct32B {
35*da0073e9SAndroid Build Coastguard Worker alignas(32) void* X;
36*da0073e9SAndroid Build Coastguard Worker };
37*da0073e9SAndroid Build Coastguard Worker #if defined(_AIX)
38*da0073e9SAndroid Build Coastguard Worker #pragma GCC diagnostic pop
39*da0073e9SAndroid Build Coastguard Worker #endif
40*da0073e9SAndroid Build Coastguard Worker } // namespace
41*da0073e9SAndroid Build Coastguard Worker static_assert(
42*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVector<void*, 0>) == sizeof(unsigned) * 2 + sizeof(void*),
43*da0073e9SAndroid Build Coastguard Worker "wasted space in SmallVector size 0");
44*da0073e9SAndroid Build Coastguard Worker static_assert(
45*da0073e9SAndroid Build Coastguard Worker alignof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
46*da0073e9SAndroid Build Coastguard Worker "wrong alignment for 16-byte aligned T");
47*da0073e9SAndroid Build Coastguard Worker static_assert(
48*da0073e9SAndroid Build Coastguard Worker alignof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
49*da0073e9SAndroid Build Coastguard Worker "wrong alignment for 32-byte aligned T");
50*da0073e9SAndroid Build Coastguard Worker static_assert(
51*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVector<Struct16B, 0>) >= alignof(Struct16B),
52*da0073e9SAndroid Build Coastguard Worker "missing padding for 16-byte aligned T");
53*da0073e9SAndroid Build Coastguard Worker static_assert(
54*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVector<Struct32B, 0>) >= alignof(Struct32B),
55*da0073e9SAndroid Build Coastguard Worker "missing padding for 32-byte aligned T");
56*da0073e9SAndroid Build Coastguard Worker static_assert(
57*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVector<void*, 1>) == sizeof(unsigned) * 2 + sizeof(void*) * 2,
58*da0073e9SAndroid Build Coastguard Worker "wasted space in SmallVector size 1");
59*da0073e9SAndroid Build Coastguard Worker
60*da0073e9SAndroid Build Coastguard Worker static_assert(
61*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVector<char, 0>) == sizeof(void*) * 2 + sizeof(void*),
62*da0073e9SAndroid Build Coastguard Worker "1 byte elements have word-sized type for size and capacity");
63*da0073e9SAndroid Build Coastguard Worker
64*da0073e9SAndroid Build Coastguard Worker /// Report that MinSize doesn't fit into this vector's size type. Throws
65*da0073e9SAndroid Build Coastguard Worker /// std::length_error or calls report_fatal_error.
66*da0073e9SAndroid Build Coastguard Worker [[noreturn]] static void report_size_overflow(size_t MinSize, size_t MaxSize);
report_size_overflow(size_t MinSize,size_t MaxSize)67*da0073e9SAndroid Build Coastguard Worker static void report_size_overflow(size_t MinSize, size_t MaxSize) {
68*da0073e9SAndroid Build Coastguard Worker std::string Reason = "SmallVector unable to grow. Requested capacity (" +
69*da0073e9SAndroid Build Coastguard Worker std::to_string(MinSize) +
70*da0073e9SAndroid Build Coastguard Worker ") is larger than maximum value for size type (" +
71*da0073e9SAndroid Build Coastguard Worker std::to_string(MaxSize) + ")";
72*da0073e9SAndroid Build Coastguard Worker throw std::length_error(Reason);
73*da0073e9SAndroid Build Coastguard Worker }
74*da0073e9SAndroid Build Coastguard Worker
75*da0073e9SAndroid Build Coastguard Worker /// Report that this vector is already at maximum capacity. Throws
76*da0073e9SAndroid Build Coastguard Worker /// std::length_error or calls report_fatal_error.
77*da0073e9SAndroid Build Coastguard Worker [[noreturn]] static void report_at_maximum_capacity(size_t MaxSize);
report_at_maximum_capacity(size_t MaxSize)78*da0073e9SAndroid Build Coastguard Worker static void report_at_maximum_capacity(size_t MaxSize) {
79*da0073e9SAndroid Build Coastguard Worker std::string Reason =
80*da0073e9SAndroid Build Coastguard Worker "SmallVector capacity unable to grow. Already at maximum size " +
81*da0073e9SAndroid Build Coastguard Worker std::to_string(MaxSize);
82*da0073e9SAndroid Build Coastguard Worker throw std::length_error(Reason);
83*da0073e9SAndroid Build Coastguard Worker }
84*da0073e9SAndroid Build Coastguard Worker
85*da0073e9SAndroid Build Coastguard Worker // Note: Moving this function into the header may cause performance regression.
86*da0073e9SAndroid Build Coastguard Worker template <class Size_T>
getNewCapacity(size_t MinSize,size_t TSize,size_t OldCapacity)87*da0073e9SAndroid Build Coastguard Worker static size_t getNewCapacity(size_t MinSize, size_t TSize, size_t OldCapacity) {
88*da0073e9SAndroid Build Coastguard Worker constexpr size_t MaxSize = std::numeric_limits<Size_T>::max();
89*da0073e9SAndroid Build Coastguard Worker
90*da0073e9SAndroid Build Coastguard Worker // Ensure we can fit the new capacity.
91*da0073e9SAndroid Build Coastguard Worker // This is only going to be applicable when the capacity is 32 bit.
92*da0073e9SAndroid Build Coastguard Worker if (MinSize > MaxSize)
93*da0073e9SAndroid Build Coastguard Worker report_size_overflow(MinSize, MaxSize);
94*da0073e9SAndroid Build Coastguard Worker
95*da0073e9SAndroid Build Coastguard Worker // Ensure we can meet the guarantee of space for at least one more element.
96*da0073e9SAndroid Build Coastguard Worker // The above check alone will not catch the case where grow is called with a
97*da0073e9SAndroid Build Coastguard Worker // default MinSize of 0, but the current capacity cannot be increased.
98*da0073e9SAndroid Build Coastguard Worker // This is only going to be applicable when the capacity is 32 bit.
99*da0073e9SAndroid Build Coastguard Worker if (OldCapacity == MaxSize)
100*da0073e9SAndroid Build Coastguard Worker report_at_maximum_capacity(MaxSize);
101*da0073e9SAndroid Build Coastguard Worker
102*da0073e9SAndroid Build Coastguard Worker // In theory 2*capacity can overflow if the capacity is 64 bit, but the
103*da0073e9SAndroid Build Coastguard Worker // original capacity would never be large enough for this to be a problem.
104*da0073e9SAndroid Build Coastguard Worker size_t NewCapacity = 2 * OldCapacity + 1; // Always grow.
105*da0073e9SAndroid Build Coastguard Worker return std::min(std::max(NewCapacity, MinSize), MaxSize);
106*da0073e9SAndroid Build Coastguard Worker }
107*da0073e9SAndroid Build Coastguard Worker
108*da0073e9SAndroid Build Coastguard Worker // Note: Moving this function into the header may cause performance regression.
109*da0073e9SAndroid Build Coastguard Worker template <class Size_T>
mallocForGrow(size_t MinSize,size_t TSize,size_t & NewCapacity)110*da0073e9SAndroid Build Coastguard Worker void* SmallVectorBase<Size_T>::mallocForGrow(
111*da0073e9SAndroid Build Coastguard Worker size_t MinSize,
112*da0073e9SAndroid Build Coastguard Worker size_t TSize,
113*da0073e9SAndroid Build Coastguard Worker size_t& NewCapacity) {
114*da0073e9SAndroid Build Coastguard Worker NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
115*da0073e9SAndroid Build Coastguard Worker // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
116*da0073e9SAndroid Build Coastguard Worker auto Result = std::malloc(NewCapacity * TSize);
117*da0073e9SAndroid Build Coastguard Worker if (Result == nullptr) {
118*da0073e9SAndroid Build Coastguard Worker throw std::bad_alloc();
119*da0073e9SAndroid Build Coastguard Worker }
120*da0073e9SAndroid Build Coastguard Worker return Result;
121*da0073e9SAndroid Build Coastguard Worker }
122*da0073e9SAndroid Build Coastguard Worker
123*da0073e9SAndroid Build Coastguard Worker // Note: Moving this function into the header may cause performance regression.
124*da0073e9SAndroid Build Coastguard Worker template <class Size_T>
grow_pod(const void * FirstEl,size_t MinSize,size_t TSize)125*da0073e9SAndroid Build Coastguard Worker void SmallVectorBase<Size_T>::grow_pod(
126*da0073e9SAndroid Build Coastguard Worker const void* FirstEl,
127*da0073e9SAndroid Build Coastguard Worker size_t MinSize,
128*da0073e9SAndroid Build Coastguard Worker size_t TSize) {
129*da0073e9SAndroid Build Coastguard Worker size_t NewCapacity = getNewCapacity<Size_T>(MinSize, TSize, this->capacity());
130*da0073e9SAndroid Build Coastguard Worker void* NewElts = nullptr;
131*da0073e9SAndroid Build Coastguard Worker if (BeginX == FirstEl) {
132*da0073e9SAndroid Build Coastguard Worker // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
133*da0073e9SAndroid Build Coastguard Worker NewElts = std::malloc(NewCapacity * TSize);
134*da0073e9SAndroid Build Coastguard Worker if (NewElts == nullptr) {
135*da0073e9SAndroid Build Coastguard Worker throw std::bad_alloc();
136*da0073e9SAndroid Build Coastguard Worker }
137*da0073e9SAndroid Build Coastguard Worker
138*da0073e9SAndroid Build Coastguard Worker // Copy the elements over. No need to run dtors on PODs.
139*da0073e9SAndroid Build Coastguard Worker memcpy(NewElts, this->BeginX, size() * TSize);
140*da0073e9SAndroid Build Coastguard Worker } else {
141*da0073e9SAndroid Build Coastguard Worker // If this wasn't grown from the inline copy, grow the allocated space.
142*da0073e9SAndroid Build Coastguard Worker // NOLINTNEXTLINE(cppcoreguidelines-no-malloc)
143*da0073e9SAndroid Build Coastguard Worker NewElts = std::realloc(this->BeginX, NewCapacity * TSize);
144*da0073e9SAndroid Build Coastguard Worker if (NewElts == nullptr) {
145*da0073e9SAndroid Build Coastguard Worker throw std::bad_alloc();
146*da0073e9SAndroid Build Coastguard Worker }
147*da0073e9SAndroid Build Coastguard Worker }
148*da0073e9SAndroid Build Coastguard Worker
149*da0073e9SAndroid Build Coastguard Worker this->BeginX = NewElts;
150*da0073e9SAndroid Build Coastguard Worker this->Capacity = NewCapacity;
151*da0073e9SAndroid Build Coastguard Worker }
152*da0073e9SAndroid Build Coastguard Worker
153*da0073e9SAndroid Build Coastguard Worker template class c10::SmallVectorBase<uint32_t>;
154*da0073e9SAndroid Build Coastguard Worker
155*da0073e9SAndroid Build Coastguard Worker // Disable the uint64_t instantiation for 32-bit builds.
156*da0073e9SAndroid Build Coastguard Worker // Both uint32_t and uint64_t instantiations are needed for 64-bit builds.
157*da0073e9SAndroid Build Coastguard Worker // This instantiation will never be used in 32-bit builds, and will cause
158*da0073e9SAndroid Build Coastguard Worker // warnings when sizeof(Size_T) > sizeof(size_t).
159*da0073e9SAndroid Build Coastguard Worker #if SIZE_MAX > UINT32_MAX
160*da0073e9SAndroid Build Coastguard Worker template class c10::SmallVectorBase<uint64_t>;
161*da0073e9SAndroid Build Coastguard Worker
162*da0073e9SAndroid Build Coastguard Worker // Assertions to ensure this #if stays in sync with SmallVectorSizeType.
163*da0073e9SAndroid Build Coastguard Worker static_assert(
164*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVectorSizeType<char>) == sizeof(uint64_t),
165*da0073e9SAndroid Build Coastguard Worker "Expected SmallVectorBase<uint64_t> variant to be in use.");
166*da0073e9SAndroid Build Coastguard Worker #else
167*da0073e9SAndroid Build Coastguard Worker static_assert(
168*da0073e9SAndroid Build Coastguard Worker sizeof(SmallVectorSizeType<char>) == sizeof(uint32_t),
169*da0073e9SAndroid Build Coastguard Worker "Expected SmallVectorBase<uint32_t> variant to be in use.");
170*da0073e9SAndroid Build Coastguard Worker #endif
171