blob: 654a1aec1ecf6884488bfeb2797fe42a245b3ebd [file] [log] [blame]
/*
* Copyright (c) 2020 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.
*/
#include "pas_config.h"
#if LIBPAS_ENABLED
#include "pas_page_base.h"
#include "pas_bitfit_page.h"
#include "pas_segregated_page.h"
pas_page_base_config* pas_page_base_get_config(pas_page_base* page)
{
switch (pas_page_base_get_config_kind(page)) {
case pas_page_config_kind_segregated:
return &pas_segregated_page_get_config(pas_page_base_get_segregated(page))->base;
case pas_page_config_kind_bitfit:
return &pas_bitfit_page_get_config(pas_page_base_get_bitfit(page))->base;
}
PAS_ASSERT(!"Should not be reached");
return NULL;
}
pas_page_granule_use_count*
pas_page_base_get_granule_use_counts(pas_page_base* page)
{
switch (pas_page_base_get_config_kind(page)) {
case pas_page_config_kind_segregated: {
pas_segregated_page* segregated_page;
segregated_page = pas_page_base_get_segregated(page);
return pas_segregated_page_get_granule_use_counts(
segregated_page, *pas_segregated_page_get_config(segregated_page));
}
case pas_page_config_kind_bitfit: {
pas_bitfit_page* bitfit_page;
bitfit_page = pas_page_base_get_bitfit(page);
return pas_bitfit_page_get_granule_use_counts(
bitfit_page, *pas_bitfit_page_get_config(bitfit_page));
} }
PAS_ASSERT(!"Should not be reached");
return NULL;
}
void pas_page_base_compute_committed_when_owned(pas_page_base* page,
pas_heap_summary* summary)
{
pas_page_granule_use_count* use_counts;
uintptr_t num_granules;
uintptr_t granule_index;
pas_page_base_config* config_ptr;
pas_page_base_config config;
config_ptr = pas_page_base_get_config(page);
config = *config_ptr;
if (config.page_size == config.granule_size) {
summary->committed += config.page_size;
return;
}
use_counts = pas_page_base_get_granule_use_counts(page);
num_granules = config.page_size / config.granule_size;
for (granule_index = num_granules; granule_index--;) {
if (use_counts[granule_index] == PAS_PAGE_GRANULE_DECOMMITTED)
summary->decommitted += config.granule_size;
else
summary->committed += config.granule_size;
}
}
bool pas_page_base_is_empty(pas_page_base* page)
{
switch (pas_page_base_get_config_kind(page)) {
case pas_page_config_kind_segregated:
return !pas_page_base_get_segregated(page)->num_non_empty_words;
case pas_page_config_kind_bitfit:
return !pas_page_base_get_bitfit(page)->num_live_bits;
}
PAS_ASSERT(!"Should not be reached");
return false;
}
void pas_page_base_add_free_range(pas_page_base* page,
pas_heap_summary* result,
pas_range range,
pas_free_range_kind kind)
{
pas_page_base_config* page_config_ptr;
pas_page_base_config page_config;
size_t* ineligible_for_decommit;
size_t* eligible_for_decommit;
size_t* decommitted;
size_t dummy;
bool empty;
pas_page_granule_use_count* use_counts;
uintptr_t first_granule_index;
uintptr_t last_granule_index;
uintptr_t granule_index;
if (pas_range_is_empty(range))
return;
PAS_ASSERT(range.end > range.begin);
page_config_ptr = pas_page_base_get_config(page);
page_config = *page_config_ptr;
PAS_ASSERT(range.end <= page_config.page_size);
empty = pas_page_base_is_empty(page);
dummy = 0; /* Tell the compiler to chill out and relax. */
switch (kind) {
case pas_free_object_range:
result->free += pas_range_size(range);
ineligible_for_decommit = &result->free_ineligible_for_decommit;
eligible_for_decommit = &result->free_eligible_for_decommit;
decommitted = &result->free_decommitted;
break;
case pas_free_meta_range:
result->meta += pas_range_size(range);
ineligible_for_decommit = &result->meta_ineligible_for_decommit;
eligible_for_decommit = &result->meta_eligible_for_decommit;
decommitted = &dummy;
break;
}
if (page_config.page_size == page_config.granule_size) {
if (empty)
(*eligible_for_decommit) += pas_range_size(range);
else
(*ineligible_for_decommit) += pas_range_size(range);
return;
}
use_counts = pas_page_base_get_granule_use_counts(page);
first_granule_index = range.begin / page_config.granule_size;
last_granule_index = (range.end - 1) / page_config.granule_size;
for (granule_index = first_granule_index;
granule_index <= last_granule_index;
++granule_index) {
pas_range granule_range;
size_t overlap_size;
granule_range = pas_range_create(granule_index * page_config.granule_size,
(granule_index + 1) * page_config.granule_size);
PAS_ASSERT(pas_range_overlaps(range, granule_range));
overlap_size = pas_range_size(pas_range_create_intersection(range,
granule_range));
switch (use_counts[granule_index]) {
case 0:
*eligible_for_decommit += overlap_size;
break;
case PAS_PAGE_GRANULE_DECOMMITTED:
*decommitted += overlap_size;
break;
default:
*ineligible_for_decommit += overlap_size;
break;
}
}
}
#endif /* LIBPAS_ENABLED */