blob: 03673147f7097aa27c86e5a516d2a065f70c9e63 [file] [log] [blame]
/*
* Copyright (c) 2018-2021 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef PAS_TRY_ALLOCATE_ARRAY_H
#define PAS_TRY_ALLOCATE_ARRAY_H
#include "pas_allocator_counts.h"
#include "pas_cares_about_size_mode.h"
#include "pas_heap.h"
#include "pas_heap_config.h"
#include "pas_heap_ref.h"
#include "pas_local_allocator_inlines.h"
#include "pas_physical_memory_transaction.h"
#include "pas_segregated_heap.h"
#include "pas_typed_allocation_result.h"
PAS_BEGIN_EXTERN_C;
/* This is for heaps that hold typed objects. These objects have a certain size. We may allocate
arrays of these objects. The type may tell us an alignment requirement in addition to a size.
The alignment requirement is taken together with the minalign argument (the allocator uses
whichever is bigger), but it's a bit more optimal to convey alignment using the alignment part
of the type than by passing it to minalign.
The entrypoints in this file are for the most general case of allocating in the typed heap. You
can pass any count that is >= 1 and any alignment that is >= 1. Note that these entrypoints have
optimizations for alignment == 1 and you get the most benefit from those optimizations if you
really pass the constant "1" for alignment (this causes the special memalign logic to be statically
killed off by the compiler). If you know that count == 1 and alignment == 1, then you're better off
using the pas_try_allocate.h entrypoints. */
static PAS_ALWAYS_INLINE pas_typed_allocation_result
pas_try_allocate_array_impl(pas_heap_ref* heap_ref,
pas_heap* heap,
size_t count,
size_t alignment,
pas_heap_config config,
pas_try_allocate_common try_allocate_common)
{
pas_heap_type* type;
size_t type_size;
size_t aligned_size;
size_t aligned_count;
unsigned allocator_index;
pas_local_allocator_result allocator;
type = heap_ref->type;
type_size = config.get_type_size(type);
if (__builtin_mul_overflow(count, type_size, &aligned_size))
return pas_typed_allocation_result_create_empty();
if (alignment == 1)
aligned_count = count;
else {
if (!pas_is_power_of_2(alignment))
return pas_typed_allocation_result_create_empty();
/* It's important that we use the right count for the lookup. This gets weird. Consider
that we might have a type of size 24, count 3, and alignment 64. 24 * 3 is 72, which is
smaller than 128, which is the size we'd want for alignment 64. The effect of this code
is to raise the count to 5, so that we try to allocate 120 bytes with alignment 64,
resulting in a 128 byte allocation.
If we didn't do this correction then we'd:
- Do the lookup with count 3. If we had already created a 64-byte aligned size class at
count 5, we'd fail to see it.
- Ask for a 64-byte aligned size class to be ensured at count 3. That would be wasteful
for any future count=3 allocations that did not want this kind of alignment. */
if (aligned_size < alignment)
aligned_size = alignment;
else
aligned_size = pas_round_up_to_power_of_2(aligned_size, alignment);
/* This is gross. In an ideal world, we wouldn't have to execute divisions. We could have
avoided this by requiring the size to be the currency used to lookup size classes.
However, since aligned allocations are rare, this is probably OK. This has the effect of
making the non-aligned case more efficient. */
aligned_count = aligned_size / type_size;
}
allocator_index = pas_segregated_heap_allocator_index_for_count_not_primitive(
&heap->segregated_heap,
aligned_count);
allocator = pas_thread_local_cache_get_local_allocator_if_can_set_cache(
allocator_index, config.config_ptr);
if (alignment != 1 && allocator.did_succeed
&& alignment > pas_local_allocator_alignment((pas_local_allocator*)allocator.allocator))
allocator.did_succeed = false;
return pas_typed_allocation_result_create_with_intrinsic_allocation_result(
try_allocate_common(heap_ref, aligned_count, aligned_size, alignment, allocator),
type, aligned_size);
}
#define PAS_CREATE_TRY_ALLOCATE_ARRAY(name, heap_config, runtime_config, allocator_counts, result_filter) \
PAS_CREATE_TRY_ALLOCATE_COMMON( \
name ## _impl, \
pas_normal_heap_ref_kind, \
(heap_config), \
(runtime_config), \
(allocator_counts), \
pas_force_count_lookup, \
(result_filter)); \
\
static PAS_ALWAYS_INLINE pas_typed_allocation_result name ## _with_heap( \
pas_heap_ref* heap_ref, pas_heap* heap, size_t count, size_t alignment) \
{ \
return pas_try_allocate_array_impl( \
heap_ref, heap, count, alignment, (heap_config), name ## _impl); \
} \
\
static PAS_ALWAYS_INLINE pas_typed_allocation_result name( \
pas_heap_ref* heap_ref, size_t count, size_t alignment) \
{ \
return name ## _with_heap( \
heap_ref, \
pas_ensure_heap( \
heap_ref, pas_normal_heap_ref_kind, (heap_config).config_ptr, (runtime_config)), \
count, alignment); \
} \
\
static PAS_UNUSED PAS_NEVER_INLINE pas_typed_allocation_result name ## _for_realloc( \
pas_heap_ref* heap_ref, pas_heap* heap, size_t count) \
{ \
return name ## _with_heap(heap_ref, heap, count, 1); \
} \
\
struct pas_dummy
typedef pas_typed_allocation_result (*pas_try_allocate_array_with_heap)(
pas_heap_ref* heap_ref, pas_heap* heap, size_t count, size_t alignment);
typedef pas_typed_allocation_result (*pas_try_allocate_array)(
pas_heap_ref* heap_ref, size_t count, size_t alignment);
typedef pas_typed_allocation_result (*pas_try_allocate_array_for_realloc)(
pas_heap_ref* heap_ref, pas_heap* heap, size_t count);
PAS_END_EXTERN_C;
#endif /* PAS_TRY_ALLOCATE_ARRAY_H */