Two patches to fix the build on msp430-elf which has 16-bit or 20-bit pointers.
The patch for 88111 also affects other targets, by changing the default values that are used when pool_options members are zero. The new default values depend on the number of bits in size_t. Bootstrapped on msp430-elf, tested on powerpc64le-linux.
commit b5ba0a7b875c3524d447452531416eabf218e6e9 Author: Jonathan Wakely <jwak...@redhat.com> Date: Wed Nov 21 18:16:45 2018 +0000 PR libstdc++/88111 Make maximum block size depend on size_t width PR libstdc++/88111 * include/std/memory_resource (pool_options): Add Doxygen comments. * src/c++17/memory_resource.cc (pool_sizes): Only use suitable values on targets with 16-bit or 20-bit size_t type. (munge_options): Make default values depend on width of size_t type. diff --git a/libstdc++-v3/include/std/memory_resource b/libstdc++-v3/include/std/memory_resource index 87ad25d60f3..e9a46a3b455 100644 --- a/libstdc++-v3/include/std/memory_resource +++ b/libstdc++-v3/include/std/memory_resource @@ -299,13 +299,25 @@ namespace pmr { return !(__a == __b); } + /// Parameters for tuning a pool resource's behaviour. struct pool_options { + /** @brief Upper limit on number of blocks in a chunk. + * + * A lower value prevents allocating huge chunks that could remain mostly + * unused, but means pools will need to replenished more frequently. + */ size_t max_blocks_per_chunk = 0; + + /* @brief Largest block size (in bytes) that should be served from pools. + * + * Larger allocations will be served directly by the upstream resource, + * not from one of the pools managed by the pool resource. + */ size_t largest_required_pool_block = 0; }; - // Common implementation details for unsynchronized/synchronized pool resources. + // Common implementation details for un-/synchronized pool resources. class __pool_resource { friend class synchronized_pool_resource; diff --git a/libstdc++-v3/src/c++17/memory_resource.cc b/libstdc++-v3/src/c++17/memory_resource.cc index 6198e6b68ca..929df93233c 100644 --- a/libstdc++-v3/src/c++17/memory_resource.cc +++ b/libstdc++-v3/src/c++17/memory_resource.cc @@ -825,10 +825,15 @@ namespace pmr 128, 192, 256, 320, 384, 448, 512, 768, +#if __SIZE_WIDTH__ > 16 1024, 1536, 2048, 3072, - 1<<12, 1<<13, 1<<14, 1<<15, 1<<16, 1<<17, +#if __SIZE_WIDTH__ > 20 + 1<<12, 1<<13, 1<<14, + 1<<15, 1<<16, 1<<17, 1<<20, 1<<21, 1<<22 // 4MB should be enough for anybody +#endif +#endif }; pool_options @@ -839,10 +844,13 @@ namespace pmr // replaced with implementation-defined defaults, and sizes may be // rounded to unspecified granularity. - // Absolute maximum. Each pool might have a smaller maximum. + // max_blocks_per_chunk sets the absolute maximum for the pool resource. + // Each pool might have a smaller maximum, because pools for very large + // objects might impose smaller limit. if (opts.max_blocks_per_chunk == 0) { - opts.max_blocks_per_chunk = 1024 * 10; // TODO a good default? + // Pick a default that depends on the number of bits in size_t. + opts.max_blocks_per_chunk = __SIZE_WIDTH__ << 8; } else { @@ -854,10 +862,15 @@ namespace pmr opts.max_blocks_per_chunk = chunk::max_blocks_per_chunk(); } - // Absolute minimum. Likely to be much larger in practice. + // largest_required_pool_block specifies the largest block size that will + // be allocated from a pool. Larger allocations will come directly from + // the upstream resource and so will not be pooled. if (opts.largest_required_pool_block == 0) { - opts.largest_required_pool_block = 4096; // TODO a good default? + // Pick a sensible default that depends on the number of bits in size_t + // (pools with larger block sizes must be explicitly requested by + // using a non-zero value for largest_required_pool_block). + opts.largest_required_pool_block = __SIZE_WIDTH__ << 6; } else { commit 14974318adc5e9d56e827cdfa39207e7c7be9e6d Author: Jonathan Wakely <jwak...@redhat.com> Date: Wed Nov 21 17:39:51 2018 +0000 PR libstdc++/88113 use size_type consistently instead of size_t On 16-bit msp430-elf size_t is either 16 bits or 20 bits, and so can't represent all values of the uint32_t type used for bitset::size_type. Using the smaller of size_t and uint32_t for size_type ensures it fits in size_t. PR libstdc++/88113 * src/c++17/memory_resource.cc (bitset::size_type): Use the smaller of uint32_t and size_t. (bitset::size(), bitset::free(), bitset::update_next_word()) (bitset::max_blocks_per_chunk(), bitset::max_word_index()): Use size_type consistently instead of size_t. (chunk): Adjust static_assert checking sizeof(chunk). diff --git a/libstdc++-v3/src/c++17/memory_resource.cc b/libstdc++-v3/src/c++17/memory_resource.cc index a3766311725..6198e6b68ca 100644 --- a/libstdc++-v3/src/c++17/memory_resource.cc +++ b/libstdc++-v3/src/c++17/memory_resource.cc @@ -252,11 +252,13 @@ namespace pmr namespace { - // Simple bitset with runtime size. Tracks used blocks in a pooled chunk. + // Simple bitset with runtime size. + // Tracks which blocks in a pool chunk are used/unused. struct bitset { using word = uint64_t; - using size_type = uint32_t; + using size_type // unsigned integer type with no more than 32 bits + = conditional_t<numeric_limits<size_t>::digits <= 32, size_t, uint32_t>; static constexpr unsigned bits_per_word = numeric_limits<word>::digits; @@ -269,7 +271,7 @@ namespace pmr __builtin_memset(_M_words, 0, last_word * sizeof(*_M_words)); // Set bits beyond _M_size, so they are not treated as free blocks: if (const size_type extra_bits = num_blocks % bits_per_word) - _M_words[last_word] = (word)-1 << extra_bits; + _M_words[last_word] = word(-1) << extra_bits; __glibcxx_assert( empty() ); __glibcxx_assert( free() == num_blocks ); } @@ -278,12 +280,12 @@ namespace pmr ~bitset() = default; // Number of blocks - size_t size() const noexcept { return _M_size; } + size_type size() const noexcept { return _M_size; } // Number of free blocks (unset bits) - size_t free() const noexcept + size_type free() const noexcept { - size_t n = 0; + size_type n = 0; for (size_type i = _M_next_word; i < nwords(); ++i) n += (bits_per_word - std::__popcount(_M_words[i])); return n; @@ -376,7 +378,7 @@ namespace pmr // this function saturates _M_next_word at max_word_index(). void update_next_word() noexcept { - size_t next = _M_next_word; + size_type next = _M_next_word; while (_M_words[next] == word(-1) && ++next < nwords()) { } _M_next_word = std::min(next, max_word_index()); @@ -397,11 +399,11 @@ namespace pmr { return (_M_size + bits_per_word - 1) / bits_per_word; } // Maximum value that can be stored in bitset::_M_size member (approx 500k) - static constexpr size_t max_blocks_per_chunk() noexcept - { return (1ull << _S_size_digits) - 1; } + static constexpr size_type max_blocks_per_chunk() noexcept + { return (size_type(1) << _S_size_digits) - 1; } // Maximum value that can be stored in bitset::_M_next_word member (8191). - static constexpr size_t max_word_index() noexcept + static constexpr size_type max_word_index() noexcept { return (max_blocks_per_chunk() + bits_per_word - 1) / bits_per_word; } word* data() const noexcept { return _M_words; } @@ -519,9 +521,12 @@ namespace pmr { return std::less<const void*>{}(p, c._M_p); } }; - // For 64-bit this is 3*sizeof(void*) and for 32-bit it's 4*sizeof(void*). + // For 64-bit pointers this is the size of three pointers i.e. 24 bytes. + // For 32-bit and 20-bit pointers it's four pointers (16 bytes). + // For 16-bit pointers it's five pointers (10 bytes). // TODO pad 64-bit to 4*sizeof(void*) to avoid splitting across cache lines? - static_assert(sizeof(chunk) == 2 * sizeof(uint32_t) + 2 * sizeof(void*)); + static_assert(sizeof(chunk) + == sizeof(bitset::size_type) + sizeof(uint32_t) + 2 * sizeof(void*)); // An oversized allocation that doesn't fit in a pool. struct big_block