Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Commit

Permalink
[Local GC] Move Software Write Watch's write barrier updates to GCToE…
Browse files Browse the repository at this point in the history
…EInterface::StompWriteBarrier (#8605)

* Move Software Write Watch's write barrier updates to use the new
GCToEEInterface::StompWriteBarrier to stomp the EE's write barrier.

* Address code review feedback, move SetCardsAfterBulkCopy to EE side of the interface
  • Loading branch information
swgillespie committed Jan 7, 2017
1 parent 548aec3 commit c10c1ff
Show file tree
Hide file tree
Showing 19 changed files with 309 additions and 188 deletions.
2 changes: 1 addition & 1 deletion src/classlibnative/bcltype/arraynative.cpp
Expand Up @@ -961,7 +961,7 @@ void memmoveGCRefs(void *dest, const void *src, size_t len)
}
}

GCHeapUtilities::GetGCHeap()->SetCardsAfterBulkCopy((Object**)dest, len);
SetCardsAfterBulkCopy((Object**)dest, len);
}

void ArrayNative::ArrayCopyNoTypeCheck(BASEARRAYREF pSrc, unsigned int srcIndex, BASEARRAYREF pDest, unsigned int destIndex, unsigned int length)
Expand Down
136 changes: 29 additions & 107 deletions src/gc/gc.cpp
Expand Up @@ -1402,9 +1402,6 @@ int mark_time, plan_time, sweep_time, reloc_time, compact_time;

#ifndef MULTIPLE_HEAPS

#define ephemeral_low g_gc_ephemeral_low
#define ephemeral_high g_gc_ephemeral_high

#endif // MULTIPLE_HEAPS

#ifdef TRACE_GC
Expand Down Expand Up @@ -2187,27 +2184,22 @@ void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_b
args.card_table = g_gc_card_table;
args.lowest_address = g_gc_lowest_address;
args.highest_address = g_gc_highest_address;
#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
if (SoftwareWriteWatch::IsEnabledForGCHeap())
{
args.write_watch_table = g_gc_sw_ww_table;
}
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
GCToEEInterface::StompWriteBarrier(&args);
}

void stomp_write_barrier_ephemeral(bool is_runtime_suspended, uint8_t* ephemeral_lo, uint8_t* ephemeral_hi)
void stomp_write_barrier_ephemeral(uint8_t* ephemeral_low, uint8_t* ephemeral_high)
{
WriteBarrierParameters args = {};
args.operation = WriteBarrierOp::StompEphemeral;
args.is_runtime_suspended = is_runtime_suspended;
args.ephemeral_lo = g_gc_ephemeral_low;
args.ephemeral_hi = g_gc_ephemeral_high;
#ifdef MULTIPLE_HEAPS
// It is not correct to update the EE's g_ephemeral_low and g_ephemeral_high
// to anything other than their default values when using Server GC, since
// there is no single ephemeral generation across all of the heaps.
// Server GC write barriers do not reference these two globals, but ErectWriteBarrier does.
//
// When MULTIPLE_HEAPS is defined, g_gc_ephemeral_low and g_gc_ephemeral_high should
// always have their default values.
assert(args.ephemeral_lo == (uint8_t*)1);
assert(args.ephemeral_hi == (uint8_t*)~0);
#endif // MULTIPLE_HEAPS
args.is_runtime_suspended = true;
args.ephemeral_low = ephemeral_low;
args.ephemeral_high = ephemeral_high;
GCToEEInterface::StompWriteBarrier(&args);
}

Expand All @@ -2220,6 +2212,8 @@ void stomp_write_barrier_initialize()
args.card_table = g_gc_card_table;
args.lowest_address = g_gc_lowest_address;
args.highest_address = g_gc_highest_address;
args.ephemeral_low = reinterpret_cast<uint8_t*>(1);
args.ephemeral_high = reinterpret_cast<uint8_t*>(~0);
GCToEEInterface::StompWriteBarrier(&args);
}

Expand Down Expand Up @@ -2430,6 +2424,10 @@ BOOL gc_heap::ro_segments_in_range;

size_t gc_heap::gen0_big_free_spaces = 0;

uint8_t* gc_heap::ephemeral_low;

uint8_t* gc_heap::ephemeral_high;

uint8_t* gc_heap::lowest_address;

uint8_t* gc_heap::highest_address;
Expand Down Expand Up @@ -7277,9 +7275,6 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
}

g_gc_card_table = translated_ct;
g_gc_lowest_address = saved_g_lowest_address;
g_gc_highest_address = saved_g_highest_address;

SoftwareWriteWatch::SetResizedUntranslatedTable(
mem + sw_ww_table_offset,
saved_g_lowest_address,
Expand All @@ -7290,6 +7285,8 @@ int gc_heap::grow_brick_card_tables (uint8_t* start,
// grow version of the write barrier. This test tells us if the new
// segment was allocated at a lower address than the old, requiring
// that we start doing an upper bounds check in the write barrier.
g_gc_lowest_address = saved_g_lowest_address;
g_gc_highest_address = saved_g_highest_address;
stomp_write_barrier_resize(true, la != saved_g_lowest_address);
write_barrier_updated = true;

Expand Down Expand Up @@ -9662,16 +9659,18 @@ void gc_heap::make_generation (generation& gen, heap_segment* seg, uint8_t* star
#endif //FREE_USAGE_STATS
}

void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended)
void gc_heap::adjust_ephemeral_limits ()
{
ephemeral_low = generation_allocation_start (generation_of (max_generation - 1));
ephemeral_high = heap_segment_reserved (ephemeral_heap_segment);

dprintf (3, ("new ephemeral low: %Ix new ephemeral high: %Ix",
(size_t)ephemeral_low, (size_t)ephemeral_high))

#ifndef MULTIPLE_HEAPS
// This updates the write barrier helpers with the new info.
stomp_write_barrier_ephemeral(is_runtime_suspended, ephemeral_low, ephemeral_high);
stomp_write_barrier_ephemeral(ephemeral_low, ephemeral_high);
#endif // MULTIPLE_HEAPS
}

#if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN)
Expand Down Expand Up @@ -10466,7 +10465,7 @@ gc_heap::init_gc_heap (int h_number)
make_background_mark_stack (b_arr);
#endif //BACKGROUND_GC

adjust_ephemeral_limits(true);
adjust_ephemeral_limits();

#ifdef MARK_ARRAY
// why would we clear the mark array for this page? it should be cleared..
Expand Down Expand Up @@ -15364,7 +15363,8 @@ void gc_heap::gc1()
if (!settings.concurrent)
#endif //BACKGROUND_GC
{
adjust_ephemeral_limits(!!IsGCThread());
assert(!!IsGCThread());
adjust_ephemeral_limits();
}

#ifdef BACKGROUND_GC
Expand Down Expand Up @@ -16204,7 +16204,8 @@ BOOL gc_heap::expand_soh_with_minimal_gc()
dd_gc_new_allocation (dynamic_data_of (max_generation)) -= ephemeral_size;
dd_new_allocation (dynamic_data_of (max_generation)) = dd_gc_new_allocation (dynamic_data_of (max_generation));

adjust_ephemeral_limits(!!IsGCThread());
assert(!!IsGCThread());
adjust_ephemeral_limits();
return TRUE;
}
else
Expand Down Expand Up @@ -32778,8 +32779,8 @@ gc_heap::verify_heap (BOOL begin_gc_p)
#endif //BACKGROUND_GC

#ifndef MULTIPLE_HEAPS
if ((g_gc_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
(g_gc_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
if ((ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) ||
(ephemeral_high != heap_segment_reserved (ephemeral_heap_segment)))
{
FATAL_GC_ERROR();
}
Expand Down Expand Up @@ -35681,85 +35682,6 @@ void GCHeap::SetFinalizationRun (Object* obj)

#endif // FEATURE_PREMORTEM_FINALIZATION

//----------------------------------------------------------------------------
//
// Write Barrier Support for bulk copy ("Clone") operations
//
// StartPoint is the target bulk copy start point
// len is the length of the bulk copy (in bytes)
//
//
// Performance Note:
//
// This is implemented somewhat "conservatively", that is we
// assume that all the contents of the bulk copy are object
// references. If they are not, and the value lies in the
// ephemeral range, we will set false positives in the card table.
//
// We could use the pointer maps and do this more accurately if necessary

#if defined(_MSC_VER) && defined(_TARGET_X86_)
#pragma optimize("y", on) // Small critical routines, don't put in EBP frame
#endif //_MSC_VER && _TARGET_X86_

void
GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len )
{
Object **rover;
Object **end;

// Target should aligned
assert(Aligned ((size_t)StartPoint));


// Don't optimize the Generation 0 case if we are checking for write barrier voilations
// since we need to update the shadow heap even in the generation 0 case.
#if defined (WRITE_BARRIER_CHECK) && !defined (SERVER_GC)
if (g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)
for(unsigned i=0; i < len / sizeof(Object*); i++)
updateGCShadow(&StartPoint[i], StartPoint[i]);
#endif //WRITE_BARRIER_CHECK && !SERVER_GC

#ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
if (SoftwareWriteWatch::IsEnabledForGCHeap())
{
SoftwareWriteWatch::SetDirtyRegion(StartPoint, len);
}
#endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP

// If destination is in Gen 0 don't bother
if (
#ifdef BACKGROUND_GC
(!gc_heap::settings.concurrent) &&
#endif //BACKGROUND_GC
(g_theGCHeap->WhichGeneration( (Object*) StartPoint ) == 0))
return;

rover = StartPoint;
end = StartPoint + (len/sizeof(Object*));
while (rover < end)
{
if ( (((uint8_t*)*rover) >= g_gc_ephemeral_low) && (((uint8_t*)*rover) < g_gc_ephemeral_high) )
{
// Set Bit For Card and advance to next card
size_t card = gcard_of ((uint8_t*)rover);

Interlocked::Or (&g_gc_card_table[card/card_word_width], (1U << (card % card_word_width)));
// Skip to next card for the object
rover = (Object**)align_on_card ((uint8_t*)(rover+1));
}
else
{
rover++;
}
}
}

#if defined(_MSC_VER) && defined(_TARGET_X86_)
#pragma optimize("", on) // Go back to command line default optimizations
#endif //_MSC_VER && _TARGET_X86_


#ifdef FEATURE_PREMORTEM_FINALIZATION

//--------------------------------------------------------------------
Expand Down
2 changes: 0 additions & 2 deletions src/gc/gc.h
Expand Up @@ -140,8 +140,6 @@ class DacHeapWalker;
extern "C" uint32_t* g_gc_card_table;
extern "C" uint8_t* g_gc_lowest_address;
extern "C" uint8_t* g_gc_highest_address;
extern "C" uint8_t* g_gc_ephemeral_low;
extern "C" uint8_t* g_gc_ephemeral_high;

namespace WKS {
::IGCHeapInternal* CreateGCHeap();
Expand Down
2 changes: 0 additions & 2 deletions src/gc/gccommon.cpp
Expand Up @@ -41,8 +41,6 @@ uint8_t* g_shadow_lowest_address = NULL;
uint32_t* g_gc_card_table;
uint8_t* g_gc_lowest_address = 0;
uint8_t* g_gc_highest_address = 0;
uint8_t* g_gc_ephemeral_low = (uint8_t*)1;
uint8_t* g_gc_ephemeral_high = (uint8_t*)~0;

VOLATILE(int32_t) m_GCLock = -1;

Expand Down
1 change: 0 additions & 1 deletion src/gc/gcimpl.h
Expand Up @@ -198,7 +198,6 @@ class GCHeap : public IGCHeapInternal
BOOL FinalizeAppDomain(AppDomain *pDomain, BOOL fRunFinalizers);
BOOL ShouldRestartFinalizerWatchDog();

void SetCardsAfterBulkCopy( Object**, size_t);
void DiagWalkObject (Object* obj, walk_fn fn, void* context);

public: // FIX
Expand Down
19 changes: 13 additions & 6 deletions src/gc/gcinterface.h
Expand Up @@ -46,7 +46,9 @@ enum class WriteBarrierOp
{
StompResize,
StompEphemeral,
Initialize
Initialize,
SwitchToWriteWatch,
SwitchToNonWriteWatch
};

// Arguments to GCToEEInterface::StompWriteBarrier
Expand Down Expand Up @@ -85,11 +87,15 @@ struct WriteBarrierParameters

// The new start of the ephemeral generation.
// Used for WriteBarrierOp::StompEphemeral.
uint8_t* ephemeral_lo;
uint8_t* ephemeral_low;

// The new end of the ephemeral generation.
// Used for WriteBarrierOp::StompEphemeral.
uint8_t* ephemeral_hi;
uint8_t* ephemeral_high;

// The new write watch table, if we are using our own write watch
// implementation. Used for WriteBarrierOp::SwitchToWriteWatch only.
uint8_t* write_watch_table;
};

#include "gcinterface.ee.h"
Expand Down Expand Up @@ -148,6 +154,10 @@ struct segment_info

#define max_generation 2

// The bit shift used to convert a memory address into an index into the
// Software Write Watch table.
#define SOFTWARE_WRITE_WATCH_AddressToTableByteIndexShift 0xc

class Object;
class IGCHeap;

Expand Down Expand Up @@ -398,9 +408,6 @@ class IGCHeap {
// sanity checks asserting that a GC has not occured.
virtual unsigned GetGcCount() = 0;

// Sets cards after an object has been memmoved.
virtual void SetCardsAfterBulkCopy(Object** obj, size_t length) = 0;

// Gets whether or not the home heap of this alloc context matches the heap
// associated with this thread.
virtual bool IsThreadUsingAllocationContextHeap(gc_alloc_context* acontext, int thread_number) = 0;
Expand Down
4 changes: 1 addition & 3 deletions src/gc/gcpriv.h
Expand Up @@ -1671,7 +1671,7 @@ class gc_heap
PER_HEAP
void reset_write_watch (BOOL concurrent_p);
PER_HEAP
void adjust_ephemeral_limits (bool is_runtime_suspended);
void adjust_ephemeral_limits ();
PER_HEAP
void make_generation (generation& gen, heap_segment* seg,
uint8_t* start, uint8_t* pointer);
Expand Down Expand Up @@ -2802,13 +2802,11 @@ class gc_heap
PER_HEAP
void exit_gc_done_event_lock();

#ifdef MULTIPLE_HEAPS
PER_HEAP
uint8_t* ephemeral_low; //lowest ephemeral address

PER_HEAP
uint8_t* ephemeral_high; //highest ephemeral address
#endif //MULTIPLE_HEAPS

PER_HEAP
uint32_t* card_table;
Expand Down
1 change: 1 addition & 0 deletions src/gc/gcsvr.cpp
Expand Up @@ -13,6 +13,7 @@
#include "gc.h"
#include "gcscan.h"
#include "gcdesc.h"
#include "softwarewritewatch.h"

#define SERVER_GC 1

Expand Down
1 change: 1 addition & 0 deletions src/gc/gcwks.cpp
Expand Up @@ -11,6 +11,7 @@
#include "gc.h"
#include "gcscan.h"
#include "gcdesc.h"
#include "softwarewritewatch.h"

#ifdef SERVER_GC
#undef SERVER_GC
Expand Down
13 changes: 5 additions & 8 deletions src/gc/sample/GCSample.cpp
Expand Up @@ -94,14 +94,11 @@ inline void ErectWriteBarrier(Object ** dst, Object * ref)
if (((uint8_t*)dst < g_gc_lowest_address) || ((uint8_t*)dst >= g_gc_highest_address))
return;

if((uint8_t*)ref >= g_gc_ephemeral_low && (uint8_t*)ref < g_gc_ephemeral_high)
{
// volatile is used here to prevent fetch of g_card_table from being reordered
// with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables.
uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst);
if(*pCardByte != 0xFF)
*pCardByte = 0xFF;
}
// volatile is used here to prevent fetch of g_card_table from being reordered
// with g_lowest/highest_address check above. See comment in code:gc_heap::grow_brick_card_tables.
uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst);
if(*pCardByte != 0xFF)
*pCardByte = 0xFF;
}

void WriteBarrier(Object ** dst, Object * ref)
Expand Down

0 comments on commit c10c1ff

Please sign in to comment.