AutoMemoryScanner.cpp [plain text]
#include "AutoAdmin.h"
#include "AutoBlockIterator.h"
#include "AutoDefs.h"
#include "AutoEnvironment.h"
#include "AutoLarge.h"
#include "AutoList.h"
#include "AutoListTypes.h"
#include "AutoRange.h"
#include "AutoMemoryScanner.h"
#include "AutoSubzone.h"
#include "AutoThread.h"
#include "AutoWriteBarrier.h"
#include "AutoZone.h"
namespace Auto {
MemoryScanner::MemoryScanner(Zone *zone, void *current_stack_bottom, bool use_write_barrier, bool does_check_block)
: _zone(zone), _current_stack_bottom(current_stack_bottom),
_is_collector(false), _use_write_barrier(use_write_barrier),
_does_check_block(does_check_block), _should_coalesce(false),
_use_exact_scanning(Environment::_agc_env._use_exact_scanning),
_coalesced_range()
{
#if defined(__ppc__)
#endif
}
void MemoryScanner::scan_for_unmarked_blocks(Subzone *subzone, usword_t q, void *block) {
Range range(block, subzone->size(q));
#if DEBUG
if (!subzone->is_start(q)) __builtin_trap();
#endif
if (subzone->layout(q) & AUTO_OBJECT) {
scan_object_range(range);
} else {
scan_range(range);
}
}
void MemoryScanner::scan_for_unmarked_blocks(Large *large, void *block) {
Range range(block, large->size());
if (large->layout() & AUTO_OBJECT) {
scan_object_range(range);
} else {
scan_range(range);
}
}
void MemoryScanner::scan_object_range(Range &block) {
if (_use_exact_scanning) {
const unsigned char *map = _zone->layout_map_for_block(block.address());
if (map) {
scan_with_layout(block, map);
return;
}
} else {
const unsigned char *map = _zone->weak_layout_map_for_block(block.address());
if (map) {
scan_with_weak_layout(block, map);
return;
}
}
scan_range(block);
}
void MemoryScanner::scan_with_layout(Range &block, const unsigned char* map) {
void **reference = (void **)block.address();
while (unsigned data = *map++) {
unsigned skip = data >> 4;
unsigned run = data & 0xf;
reference += skip;
scan_range(reference, reference + run);
reference += run;
}
scan_range((void *)reference, block.end());
}
void MemoryScanner::scan_with_weak_layout(Range &block, const unsigned char* map) {
void **reference = (void **)block.address();
unsigned char data;
while (data = *map++) {
unsigned run = data >> 4;
unsigned skip = data & 0xf;
scan_range(reference, reference + run);
reference += run;
reference += skip;
}
scan_range((void *)reference, block.end());
}
void MemoryScanner::set_pending(void *block) {
#if defined(DEBUG)
_blocks_checked++;
#endif
if (_zone->set_pending(block)) {
_blocks_scanned++;
}
}
void MemoryScanner::check_block(void **reference, void *block) {
set_pending(block);
}
#if defined(__ppc__)
inline vector unsigned int MemoryScanner::isolate_altivec_vector(vector unsigned int data_vector, vector unsigned int lo_vector, vector unsigned int diff_vector) {
vector unsigned int biased_vector = vec_sub(data_vector, lo_vector);
vector unsigned int compare_vector = vec_cmpgt(diff_vector, biased_vector);
return vec_and(data_vector, compare_vector);
}
inline void MemoryScanner::check_altivec_vector(void **isolated_vector) {
void *result0 = isolated_vector[0];
void *result1 = isolated_vector[1];
void *result2 = isolated_vector[2];
void *result3 = isolated_vector[3];
if (result0) set_pending(result0);
if (result1) set_pending(result1);
if (result2) set_pending(result2);
if (result3) set_pending(result3);
}
void MemoryScanner::scan_range_altivec(void **reference, void **end) {
typedef union {
vector unsigned int _as_vector;
void *_as_pointer[4];
} ConvertBuffer;
vector unsigned int *reference_vector = (vector unsigned int *)reference;
vector unsigned int *end_vector = (vector unsigned int *)end;
vector unsigned int data_vector0 = *reference_vector++; vector unsigned int data_vector1;
vector unsigned int *near_end_vector = end_vector - 1;
vector unsigned int lo_vector = _lo_vector;
vector unsigned int diff_vector = _diff_vector;
ConvertBuffer buffer0 = { NULL, NULL, NULL, NULL };
ConvertBuffer buffer1 = { NULL, NULL, NULL, NULL };
while (reference_vector < near_end_vector) {
data_vector1 = *reference_vector++;
buffer1._as_vector = isolate_altivec_vector(data_vector0, lo_vector, diff_vector);
check_altivec_vector(buffer0._as_pointer);
data_vector0 = *reference_vector++;
buffer0._as_vector = isolate_altivec_vector(data_vector1, lo_vector, diff_vector);
check_altivec_vector(buffer1._as_pointer);
}
buffer1._as_vector = isolate_altivec_vector(data_vector0, lo_vector, diff_vector);
check_altivec_vector(buffer0._as_pointer);
if (reference_vector < end_vector) {
buffer0._as_vector = isolate_altivec_vector(*reference_vector, lo_vector, diff_vector);
check_altivec_vector(buffer1._as_pointer);
check_altivec_vector(buffer0._as_pointer);
} else {
check_altivec_vector(buffer1._as_pointer);
}
}
#endif
void MemoryScanner::scan_range(const Range &range, WriteBarrier *wb) {
void **reference = (void **)range.address();
void **end = (void **)range.end();
if (_should_coalesce) {
if (_coalesced_range.end() == (void *)reference) {
_coalesced_range.set_end(end);
return;
}
reference = (void **)_coalesced_range.address();
end = (void **)_coalesced_range.end();
_coalesced_range = range;
}
if (reference == end) return;
if (!_zone->use_pending()) {
const usword_t scan_maximum = 1024;
usword_t size = (uintptr_t)end - (uintptr_t)reference;
if (size > scan_maximum) {
end = (void **)displace((void *)reference, scan_maximum);
Range tail(end, size - scan_maximum);
_zone->scan_stack_push_range(tail);
}
}
_amount_scanned += (char *)end - (char *)reference;
__builtin_prefetch(reference);
uintptr_t valid_lowest = (uintptr_t)_zone->coverage().address();
uintptr_t valid_size = (uintptr_t)_zone->coverage().end() - valid_lowest;
if (UseArena || wb || _does_check_block || (((uintptr_t)reference | (uintptr_t)end) & 15)) {
void *last_valid_pointer = end - 1;
for ( ; reference <= last_valid_pointer; reference = (void **)((usword_t)reference + 4)) {
void *referent = *reference;
if (((intptr_t)referent - valid_lowest) < valid_size && _zone->block_is_start(referent)) {
check_block(reference, referent);
if (wb && _zone->block_is_new(referent))
wb->mark_card(reference);
}
}
} else
#if defined(__ppc__)
if (has_altivec()) {
scan_range_altivec(reference, end);
} else
#endif
{
while (reference < end) {
void *referent0 = reference[0];
void *referent1 = reference[1];
void *referent2 = reference[2];
void *referent3 = reference[3];
reference += 4; __builtin_prefetch(reference);
if (((intptr_t)referent0 - valid_lowest) < valid_size) set_pending(referent0);
if (((intptr_t)referent1 - valid_lowest) < valid_size) set_pending(referent1);
if (((intptr_t)referent2 - valid_lowest) < valid_size) set_pending(referent2);
if (((intptr_t)referent3 - valid_lowest) < valid_size) set_pending(referent3);
}
}
}
extern "C" {
void scanMemory(void *context, void *start, void *end) {
MemoryScanner *scanner = (MemoryScanner *)context;
Range r(start, end);
scanner->scan_range(r);
}
}
void MemoryScanner::scan_external() {
if (_zone->control.scan_external_callout) _zone->control.scan_external_callout((void *)this, scanMemory);
}
void MemoryScanner::scan_range_from_thread(Range &range, Thread *thread) {
scan_range(range);
}
void MemoryScanner::scan_range_from_registers(Range &range, Thread *thread, int first_register) {
scan_range(range);
}
struct scan_retained_blocks_visitor {
MemoryScanner &_scanner;
scan_retained_blocks_visitor(MemoryScanner &scanner) : _scanner(scanner) {}
inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
if (subzone->has_refcount(q) && !subzone->test_set_mark(q)) {
if (subzone->layout(q) & AUTO_UNSCANNED) return true;
if (zone->use_pending()) {
subzone->set_pending(q);
} else {
zone->scan_stack_push_block(subzone->quantum_address(q));
}
}
return true;
}
inline bool visit(Zone *zone, Large *large) {
if (large->refcount() && !large->test_set_mark()) {
if (large->layout() & AUTO_UNSCANNED) return true;
if (zone->use_pending()) {
large->set_pending();
} else {
zone->scan_stack_push_block(large->address());
}
}
return true;
}
};
void MemoryScanner::scan_retained_blocks() {
scan_retained_blocks_visitor visitor(*this);
BlockIterator<scan_retained_blocks_visitor> iterator(_zone, visitor);
iterator.visit();
_zone->set_some_pending();
}
struct scan_retained_and_old_blocks_visitor {
MemoryScanner &_scanner;
scan_retained_and_old_blocks_visitor(MemoryScanner &scanner) : _scanner(scanner) {}
inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
if (subzone->is_new(q)) {
if (subzone->has_refcount(q) && !subzone->test_set_mark(q) && subzone->is_scanned(q)) {
if (zone->use_pending()) {
subzone->set_pending(q);
} else {
zone->scan_stack_push_block(subzone->quantum_address(q));
}
}
} else {
subzone->set_mark(q);
if (subzone->is_scanned(q)) {
WriteBarrier& wb = subzone->write_barrier();
wb.scan_ranges(subzone->quantum_address(q), subzone->size(q), _scanner);
}
}
return true;
}
inline bool visit(Zone *zone, Large *large) {
if (large->is_new()) {
if (large->refcount() && !large->test_set_mark() && large->is_scanned()) {
if (zone->use_pending()) {
large->set_pending();
} else {
zone->scan_stack_push_block(large->address());
}
}
} else {
large->set_mark();
if (large->is_scanned()) {
WriteBarrier& wb = large->write_barrier();
wb.scan_ranges(large->address(), large->size(), _scanner);
}
}
return true;
}
};
void MemoryScanner::scan_retained_and_old_blocks() {
scan_retained_and_old_blocks_visitor visitor(*this);
BlockIterator<scan_retained_and_old_blocks_visitor> iterator(_zone, visitor);
iterator.visit();
_zone->set_some_pending();
}
void MemoryScanner::scan_root_ranges() {
PointerList &list = _zone->garbage_list();
_zone->copy_roots(list);
void **roots = (void **)list.buffer();
for (usword_t i = 0, count = list.count(); i < count; i++) {
Range range(roots[i], sizeof(void*));
scan_range(range);
}
}
void MemoryScanner::scan_thread_ranges(thread_scan_t scan_type) {
if (scan_type == scan_without_suspend) {
for (Thread *thread = _zone->threads(); thread; thread = thread->next()) {
thread->scan_thread_without_suspend(*this);
}
} else {
for (Thread *thread = _zone->threads(); thread; thread = thread->next()) {
if (!thread->is_current_thread()) {
thread->scan_thread_with_suspend_and_closure(*this);
}
}
}
}
void MemoryScanner::check_roots() {
scan_retained_blocks();
scan_root_ranges();
}
struct scan_pending_blocks_visitor {
MemoryScanner &_scanner;
scan_pending_blocks_visitor(MemoryScanner &scanner) : _scanner(scanner) {}
inline bool visit(Zone *zone, Subzone *subzone, usword_t q) {
if (subzone->is_pending(q)) {
subzone->clear_pending(q);
_scanner.scan_for_unmarked_blocks(subzone, q, subzone->quantum_address(q));
}
return true;
}
inline bool visit(Zone *zone, Large *large) {
if (large->is_pending()) {
large->clear_pending();
_scanner.scan_for_unmarked_blocks(large, large->address());
}
return true;
}
};
void MemoryScanner::scan_pending_blocks() {
scan_pending_blocks_visitor visitor(*this);
_should_coalesce = true;
visitAllocatedBlocks(_zone, visitor);
scan_range((void *)NULL, (void *)NULL);
_should_coalesce = false;
}
void MemoryScanner::scan_pending_until_done() {
if (_zone->use_pending()) {
while (_zone->is_some_pending()) {
_zone->clear_some_pending();
scan_pending_blocks();
}
} else {
while (!_zone->scan_stack_is_empty()) {
if (_zone->scan_stack_is_range()) {
Range range = _zone->scan_stack_pop_range();
scan_range(range);
} else {
void *block = _zone->scan_stack_pop_block();
if (_zone->in_subzone_memory(block)) {
Subzone *subzone = Subzone::subzone(block);
usword_t q = subzone->quantum_index(block);
scan_for_unmarked_blocks(subzone, q, block);
} else {
Large *large = Large::large(block);
scan_for_unmarked_blocks(large, block);
}
}
}
}
}
void MemoryScanner::scan() {
_amount_scanned = 0;
_blocks_scanned = 0;
#if defined(DEBUG)
_blocks_checked = 0;
uint64_t start_time = micro_time();
#endif
check_roots();
SpinLock threads_lock(_zone->threads_lock());
scan_thread_ranges(scan_without_suspend);
scan_external();
scan_pending_until_done();
#if defined(DEBUG)
uint64_t suspend_start_time = micro_time();
#endif
scan_thread_ranges(scan_with_suspend_and_closure);
#if defined(DEBUG)
unsigned suspend_end_time = (unsigned)(micro_time() - suspend_start_time);
#endif
scan_barrier();
_zone->scan_associations(*this);
#if defined(DEBUG)
unsigned end_time = (unsigned)(micro_time() - start_time);
unsigned thread_count = 0;
for (Thread *thread = _zone->threads(); thread; thread = thread->next()) thread_count++;
if (Environment::_agc_env._print_scan_stats) {
printf("%s scan %10u (%3lu%%) bytes in %5u usecs, %6u blocks checked, %6u (%3lu%%) blocks scanned, %4u usecs per %3u threads\n",
_use_write_barrier ? "Partial" : "Full ",
(unsigned)_amount_scanned, (unsigned)_amount_scanned * 100 / _zone->statistics().size(),
end_time,
(unsigned)_blocks_checked,
(unsigned)_blocks_scanned, (unsigned)_blocks_scanned * 100 / _zone->statistics().count(),
suspend_end_time/thread_count, thread_count);
}
#endif
}
void MemoryScanner::scan_barrier() {}
};