From 994e7b824992f2b73174983a931775b104a20dda Mon Sep 17 00:00:00 2001 From: Sean Gillespie Date: Sat, 10 Dec 2016 17:50:06 -0800 Subject: [PATCH] Local GC: Decouple write barrier operations between the GC and EE (dotnet/coreclr#8568) * Decouple write barrier operations between the GC and EE * Address code review feedback * Address code review feedback * Repair the standalone GC build Commit migrated from https://github.com/dotnet/coreclr/commit/04d6bd105ade5f6189a15a6dbb59b082613429a1 --- src/coreclr/src/gc/env/gcenv.ee.h | 1 + src/coreclr/src/gc/gc.cpp | 256 +++++++++++++++++------------ src/coreclr/src/gc/gc.h | 6 + src/coreclr/src/gc/gccommon.cpp | 16 +- src/coreclr/src/gc/gcenv.ee.standalone.inl | 7 + src/coreclr/src/gc/gcinterface.ee.h | 5 + src/coreclr/src/gc/gcinterface.h | 64 ++++++-- src/coreclr/src/gc/gcpriv.h | 3 - src/coreclr/src/gc/sample/GCSample.cpp | 6 +- src/coreclr/src/gc/sample/gcenv.ee.cpp | 12 +- src/coreclr/src/vm/gcenv.ee.cpp | 54 ++++++ src/coreclr/src/vm/gcenv.ee.h | 1 + src/coreclr/src/vm/gcheaputilities.cpp | 10 ++ src/coreclr/src/vm/gcheaputilities.h | 13 ++ 14 files changed, 311 insertions(+), 143 deletions(-) diff --git a/src/coreclr/src/gc/env/gcenv.ee.h b/src/coreclr/src/gc/env/gcenv.ee.h index 5588698..beb0c1a 100644 --- a/src/coreclr/src/gc/env/gcenv.ee.h +++ b/src/coreclr/src/gc/env/gcenv.ee.h @@ -65,6 +65,7 @@ public: static void DiagWalkSurvivors(void* gcContext); static void DiagWalkLOHSurvivors(void* gcContext); static void DiagWalkBGCSurvivors(void* gcContext); + static void StompWriteBarrier(WriteBarrierParameters* args); }; #endif // __GCENV_EE_H__ diff --git a/src/coreclr/src/gc/gc.cpp b/src/coreclr/src/gc/gc.cpp index 9d1736c..6187938 100644 --- a/src/coreclr/src/gc/gc.cpp +++ b/src/coreclr/src/gc/gc.cpp @@ -333,8 +333,8 @@ void gc_heap::add_to_history_per_heap() #endif //BACKGROUND_GC current_hist->fgc_lowest = lowest_address; current_hist->fgc_highest = highest_address; - current_hist->g_lowest = g_lowest_address; - current_hist->g_highest = g_highest_address; + current_hist->g_lowest = g_gc_lowest_address; + current_hist->g_highest = g_gc_highest_address; gchist_index_per_heap++; if (gchist_index_per_heap == max_history_count) @@ -1402,8 +1402,8 @@ int mark_time, plan_time, sweep_time, reloc_time, compact_time; #ifndef MULTIPLE_HEAPS -#define ephemeral_low g_ephemeral_low -#define ephemeral_high g_ephemeral_high +#define ephemeral_low g_gc_ephemeral_low +#define ephemeral_high g_gc_ephemeral_high #endif // MULTIPLE_HEAPS @@ -2176,6 +2176,55 @@ int log2(unsigned int n) return pos; } +#ifndef DACCESS_COMPILE + +void stomp_write_barrier_resize(bool is_runtime_suspended, bool requires_upper_bounds_check) +{ + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::StompResize; + args.is_runtime_suspended = is_runtime_suspended; + args.requires_upper_bounds_check = requires_upper_bounds_check; + args.card_table = g_gc_card_table; + args.lowest_address = g_gc_lowest_address; + args.highest_address = g_gc_highest_address; + GCToEEInterface::StompWriteBarrier(&args); +} + +void stomp_write_barrier_ephemeral(bool is_runtime_suspended, uint8_t* ephemeral_lo, uint8_t* ephemeral_hi) +{ + 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 + GCToEEInterface::StompWriteBarrier(&args); +} + +void stomp_write_barrier_initialize() +{ + WriteBarrierParameters args = {}; + args.operation = WriteBarrierOp::Initialize; + args.is_runtime_suspended = true; + args.requires_upper_bounds_check = false; + args.card_table = g_gc_card_table; + args.lowest_address = g_gc_lowest_address; + args.highest_address = g_gc_highest_address; + GCToEEInterface::StompWriteBarrier(&args); +} + +#endif // DACCESS_COMPILE + //extract the low bits [0,low[ of a uint32_t #define lowbits(wrd, bits) ((wrd) & ((1 << (bits))-1)) //extract the high bits [high, 32] of a uint32_t @@ -3406,7 +3455,7 @@ inline size_t ro_seg_begin_index (heap_segment* seg) { size_t begin_index = (size_t)seg / gc_heap::min_segment_size; - begin_index = max (begin_index, (size_t)g_lowest_address / gc_heap::min_segment_size); + begin_index = max (begin_index, (size_t)g_gc_lowest_address / gc_heap::min_segment_size); return begin_index; } @@ -3414,14 +3463,14 @@ inline size_t ro_seg_end_index (heap_segment* seg) { size_t end_index = (size_t)(heap_segment_reserved (seg) - 1) / gc_heap::min_segment_size; - end_index = min (end_index, (size_t)g_highest_address / gc_heap::min_segment_size); + end_index = min (end_index, (size_t)g_gc_highest_address / gc_heap::min_segment_size); return end_index; } void seg_mapping_table_add_ro_segment (heap_segment* seg) { #ifdef GROWABLE_SEG_MAPPING_TABLE - if ((heap_segment_reserved (seg) <= g_lowest_address) || (heap_segment_mem (seg) >= g_highest_address)) + if ((heap_segment_reserved (seg) <= g_gc_lowest_address) || (heap_segment_mem (seg) >= g_gc_highest_address)) return; #endif //GROWABLE_SEG_MAPPING_TABLE @@ -3605,7 +3654,7 @@ gc_heap* seg_mapping_table_heap_of_worker (uint8_t* o) gc_heap* seg_mapping_table_heap_of (uint8_t* o) { #ifdef GROWABLE_SEG_MAPPING_TABLE - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) return 0; #endif //GROWABLE_SEG_MAPPING_TABLE @@ -3615,7 +3664,7 @@ gc_heap* seg_mapping_table_heap_of (uint8_t* o) gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o) { #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) return 0; #endif //FEATURE_BASICFREEZE || GROWABLE_SEG_MAPPING_TABLE @@ -3627,7 +3676,7 @@ gc_heap* seg_mapping_table_heap_of_gc (uint8_t* o) heap_segment* seg_mapping_table_segment_of (uint8_t* o) { #if defined(FEATURE_BASICFREEZE) && defined(GROWABLE_SEG_MAPPING_TABLE) - if ((o < g_lowest_address) || (o >= g_highest_address)) + if ((o < g_gc_lowest_address) || (o >= g_gc_highest_address)) #ifdef FEATURE_BASICFREEZE return ro_segment_lookup (o); #else @@ -3670,7 +3719,7 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o) #ifdef FEATURE_BASICFREEZE // TODO: This was originally written assuming that the seg_mapping_table would always contain entries for ro - // segments whenever the ro segment falls into the [g_lowest_address,g_highest_address) range. I.e., it had an + // segments whenever the ro segment falls into the [g_gc_lowest_address,g_gc_highest_address) range. I.e., it had an // extra "&& (size_t)(entry->seg1) & ro_in_entry" expression. However, at the moment, grow_brick_card_table does // not correctly go through the ro segments and add them back to the seg_mapping_table when the [lowest,highest) // range changes. We should probably go ahead and modify grow_brick_card_table and put back the @@ -4070,8 +4119,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h memory_details.current_block_normal = 0; memory_details.current_block_large = 0; - g_lowest_address = MAX_PTR; - g_highest_address = 0; + g_gc_lowest_address = MAX_PTR; + g_gc_highest_address = 0; if (((size_t)MAX_PTR - large_size) < normal_size) { @@ -4091,8 +4140,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h uint8_t* allatonce_block = (uint8_t*)virtual_alloc (requestedMemory); if (allatonce_block) { - g_lowest_address = allatonce_block; - g_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size)); + g_gc_lowest_address = allatonce_block; + g_gc_highest_address = allatonce_block + (memory_details.block_count * (large_size + normal_size)); memory_details.allocation_pattern = initial_memory_details::ALLATONCE; for(size_t i = 0; i < memory_details.block_count; i++) @@ -4115,8 +4164,8 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h if (b2) { memory_details.allocation_pattern = initial_memory_details::TWO_STAGE; - g_lowest_address = min(b1,b2); - g_highest_address = max(b1 + memory_details.block_count*normal_size, + g_gc_lowest_address = min(b1,b2); + g_gc_highest_address = max(b1 + memory_details.block_count*normal_size, b2 + memory_details.block_count*large_size); for(size_t i = 0; i < memory_details.block_count; i++) { @@ -4162,10 +4211,10 @@ BOOL reserve_initial_memory (size_t normal_size, size_t large_size, size_t num_h } else { - if (current_block->memory_base < g_lowest_address) - g_lowest_address = current_block->memory_base; - if (((uint8_t *) current_block->memory_base + block_size) > g_highest_address) - g_highest_address = (current_block->memory_base + block_size); + if (current_block->memory_base < g_gc_lowest_address) + g_gc_lowest_address = current_block->memory_base; + if (((uint8_t *) current_block->memory_base + block_size) > g_gc_highest_address) + g_gc_highest_address = (current_block->memory_base + block_size); } reserve_success = TRUE; } @@ -4607,22 +4656,22 @@ gc_heap::get_segment (size_t size, BOOL loh_p) { uint8_t* start; uint8_t* end; - if (mem < g_lowest_address) + if (mem < g_gc_lowest_address) { start = (uint8_t*)mem; } else { - start = (uint8_t*)g_lowest_address; + start = (uint8_t*)g_gc_lowest_address; } - if (((uint8_t*)mem + size) > g_highest_address) + if (((uint8_t*)mem + size) > g_gc_highest_address) { end = (uint8_t*)mem + size; } else { - end = (uint8_t*)g_highest_address; + end = (uint8_t*)g_gc_highest_address; } if (gc_heap::grow_brick_card_tables (start, end, size, result, __this, loh_p) != 0) @@ -5321,7 +5370,7 @@ heap_segment* gc_heap::segment_of (uint8_t* add, ptrdiff_t& delta, BOOL verify_p uint8_t* sadd = add; heap_segment* hs = 0; heap_segment* hs1 = 0; - if (!((add >= g_lowest_address) && (add < g_highest_address))) + if (!((add >= g_gc_lowest_address) && (add < g_gc_highest_address))) { delta = 0; return 0; @@ -6378,7 +6427,7 @@ void gc_heap::set_card (size_t card) inline void gset_card (size_t card) { - g_card_table [card_word (card)] |= (1 << card_bit (card)); + g_gc_card_table [card_word (card)] |= (1 << card_bit (card)); } inline @@ -6489,7 +6538,7 @@ size_t size_card_bundle_of (uint8_t* from, uint8_t* end) uint32_t* translate_card_bundle_table (uint32_t* cb) { - return (uint32_t*)((uint8_t*)cb - ((((size_t)g_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t))); + return (uint32_t*)((uint8_t*)cb - ((((size_t)g_gc_lowest_address) / (card_size*card_word_width*card_bundle_size*card_bundle_word_width)) * sizeof (uint32_t))); } void gc_heap::enable_card_bundles () @@ -6701,7 +6750,7 @@ size_t size_mark_array_of (uint8_t* from, uint8_t* end) // according to the lowest_address. uint32_t* translate_mark_array (uint32_t* ma) { - return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_lowest_address)); + return (uint32_t*)((uint8_t*)ma - size_mark_array_of (0, g_gc_lowest_address)); } // from and end must be page aligned addresses. @@ -6829,16 +6878,16 @@ void release_card_table (uint32_t* c_table) { destroy_card_table (c_table); // sever the link from the parent - if (&g_card_table[card_word (gcard_of(g_lowest_address))] == c_table) + if (&g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))] == c_table) { - g_card_table = 0; + g_gc_card_table = 0; #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP SoftwareWriteWatch::StaticClose(); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP } else { - uint32_t* p_table = &g_card_table[card_word (gcard_of(g_lowest_address))]; + uint32_t* p_table = &g_gc_card_table[card_word (gcard_of(g_gc_lowest_address))]; if (p_table) { while (p_table && (card_table_next (p_table) != c_table)) @@ -6860,8 +6909,8 @@ void destroy_card_table (uint32_t* c_table) uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) { - assert (g_lowest_address == start); - assert (g_highest_address == end); + assert (g_gc_lowest_address == start); + assert (g_gc_highest_address == end); uint32_t virtual_reserve_flags = VirtualReserveFlags::None; @@ -6881,7 +6930,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) if (can_use_write_watch_for_card_table()) { virtual_reserve_flags |= VirtualReserveFlags::WriteWatch; - cb = size_card_bundle_of (g_lowest_address, g_highest_address); + cb = size_card_bundle_of (g_gc_lowest_address, g_gc_highest_address); } #endif //CARD_BUNDLE @@ -6897,7 +6946,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP #ifdef GROWABLE_SEG_MAPPING_TABLE - size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address); + size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); size_t st_table_offset = sizeof(card_table_info) + cs + bs + cb + wws; size_t st_table_offset_aligned = align_for_seg_mapping_table (st_table_offset); @@ -6952,7 +7001,7 @@ uint32_t* gc_heap::make_card_table (uint8_t* start, uint8_t* end) #ifdef GROWABLE_SEG_MAPPING_TABLE seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); seg_mapping_table = (seg_mapping*)((uint8_t*)seg_mapping_table - - size_seg_mapping_table_of (0, (align_lower_segment (g_lowest_address)))); + size_seg_mapping_table_of (0, (align_lower_segment (g_gc_lowest_address)))); #endif //GROWABLE_SEG_MAPPING_TABLE #ifdef MARK_ARRAY @@ -6991,10 +7040,10 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, gc_heap* hp, BOOL loh_p) { - uint8_t* la = g_lowest_address; - uint8_t* ha = g_highest_address; - uint8_t* saved_g_lowest_address = min (start, g_lowest_address); - uint8_t* saved_g_highest_address = max (end, g_highest_address); + uint8_t* la = g_gc_lowest_address; + uint8_t* ha = g_gc_highest_address; + uint8_t* saved_g_lowest_address = min (start, g_gc_lowest_address); + uint8_t* saved_g_highest_address = max (end, g_gc_highest_address); #ifdef BACKGROUND_GC // This value is only for logging purpose - it's not necessarily exactly what we // would commit for mark array but close enough for diagnostics purpose. @@ -7024,18 +7073,18 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, #endif // BIT64 ps *= 2; - if (saved_g_lowest_address < g_lowest_address) + if (saved_g_lowest_address < g_gc_lowest_address) { - if (ps > (size_t)g_lowest_address) + if (ps > (size_t)g_gc_lowest_address) saved_g_lowest_address = (uint8_t*)OS_PAGE_SIZE; else { - assert (((size_t)g_lowest_address - ps) >= OS_PAGE_SIZE); - saved_g_lowest_address = min (saved_g_lowest_address, (g_lowest_address - ps)); + assert (((size_t)g_gc_lowest_address - ps) >= OS_PAGE_SIZE); + saved_g_lowest_address = min (saved_g_lowest_address, (g_gc_lowest_address - ps)); } } - if (saved_g_highest_address > g_highest_address) + if (saved_g_highest_address > g_gc_highest_address) { saved_g_highest_address = max ((saved_g_lowest_address + ps), saved_g_highest_address); if (saved_g_highest_address > top) @@ -7048,7 +7097,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, bool write_barrier_updated = false; uint32_t virtual_reserve_flags = VirtualReserveFlags::None; - uint32_t* saved_g_card_table = g_card_table; + uint32_t* saved_g_card_table = g_gc_card_table; uint32_t* ct = 0; uint32_t* translated_ct = 0; short* bt = 0; @@ -7131,7 +7180,7 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, card_table_refcount (ct) = 0; card_table_lowest_address (ct) = saved_g_lowest_address; card_table_highest_address (ct) = saved_g_highest_address; - card_table_next (ct) = &g_card_table[card_word (gcard_of (la))]; + card_table_next (ct) = &g_gc_card_table[card_word (gcard_of (la))]; //clear the card table /* @@ -7158,9 +7207,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, seg_mapping* new_seg_mapping_table = (seg_mapping*)(mem + st_table_offset_aligned); new_seg_mapping_table = (seg_mapping*)((uint8_t*)new_seg_mapping_table - size_seg_mapping_table_of (0, (align_lower_segment (saved_g_lowest_address)))); - memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_lowest_address)], - &seg_mapping_table[seg_mapping_word_of(g_lowest_address)], - size_seg_mapping_table_of(g_lowest_address, g_highest_address)); + memcpy(&new_seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], + &seg_mapping_table[seg_mapping_word_of(g_gc_lowest_address)], + size_seg_mapping_table_of(g_gc_lowest_address, g_gc_highest_address)); seg_mapping_table = new_seg_mapping_table; } @@ -7222,12 +7271,14 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, // Note on points where the runtime is suspended anywhere in this function. Upon an attempt to suspend the // runtime, a different thread may suspend first, causing this thread to block at the point of the suspend call. // So, at any suspend point, externally visible state needs to be consistent, as code that depends on that state - // may run while this thread is blocked. This includes updates to g_card_table, g_lowest_address, and - // g_highest_address. + // may run while this thread is blocked. This includes updates to g_gc_card_table, g_gc_lowest_address, and + // g_gc_highest_address. suspend_EE(); } - g_card_table = translated_ct; + 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, @@ -7239,7 +7290,7 @@ 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. - StompWriteBarrierResize(true, la != saved_g_lowest_address); + stomp_write_barrier_resize(true, la != saved_g_lowest_address); write_barrier_updated = true; if (!is_runtime_suspended) @@ -7250,9 +7301,12 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, else #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP { - g_card_table = translated_ct; + g_gc_card_table = translated_ct; } + g_gc_lowest_address = saved_g_lowest_address; + g_gc_highest_address = saved_g_highest_address; + if (!write_barrier_updated) { // This passes a bool telling whether we need to switch to the post @@ -7263,19 +7317,9 @@ int gc_heap::grow_brick_card_tables (uint8_t* start, // to be changed, so we are doing this after all global state has // been updated. See the comment above suspend_EE() above for more // info. - StompWriteBarrierResize(!!IsGCThread(), la != saved_g_lowest_address); + stomp_write_barrier_resize(!!IsGCThread(), la != saved_g_lowest_address); } - // We need to make sure that other threads executing checked write barriers - // will see the g_card_table update before g_lowest/highest_address updates. - // Otherwise, the checked write barrier may AV accessing the old card table - // with address that it does not cover. Write barriers access card table - // without memory barriers for performance reasons, so we need to flush - // the store buffers here. - GCToOSInterface::FlushProcessWriteBuffers(); - - g_lowest_address = saved_g_lowest_address; - VolatileStore(&g_highest_address, saved_g_highest_address); return 0; @@ -7284,7 +7328,7 @@ fail: if (mem) { - assert(g_card_table == saved_g_card_table); + assert(g_gc_card_table == saved_g_card_table); //delete (uint32_t*)((uint8_t*)ct - sizeof(card_table_info)); if (!GCToOSInterface::VirtualRelease (mem, alloc_size_aligned)) @@ -7442,7 +7486,7 @@ void gc_heap::copy_brick_card_table() assert (ha == card_table_highest_address (&old_card_table[card_word (card_of (la))])); /* todo: Need a global lock for this */ - uint32_t* ct = &g_card_table[card_word (gcard_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; own_card_table (ct); card_table = translate_card_table (ct); /* End of global lock */ @@ -7455,8 +7499,8 @@ void gc_heap::copy_brick_card_table() if (gc_can_use_concurrent) { mark_array = translate_mark_array (card_table_mark_array (ct)); - assert (mark_word_of (g_highest_address) == - mark_word_of (align_on_mark_word (g_highest_address))); + assert (mark_word_of (g_gc_highest_address) == + mark_word_of (align_on_mark_word (g_gc_highest_address))); } else mark_array = NULL; @@ -7465,13 +7509,13 @@ void gc_heap::copy_brick_card_table() #ifdef CARD_BUNDLE #if defined(MARK_ARRAY) && defined(_DEBUG) #ifdef GROWABLE_SEG_MAPPING_TABLE - size_t st = size_seg_mapping_table_of (g_lowest_address, g_highest_address); + size_t st = size_seg_mapping_table_of (g_gc_lowest_address, g_gc_highest_address); #else //GROWABLE_SEG_MAPPING_TABLE size_t st = 0; #endif //GROWABLE_SEG_MAPPING_TABLE #endif //MARK_ARRAY && _DEBUG card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct)); - assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] == + assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == card_table_card_bundle_table (ct)); //set the card table if we are in a heap growth scenario @@ -9315,7 +9359,7 @@ void gc_heap::update_card_table_bundle() { size_t bcardw = (uint32_t*)(max(g_addresses[i],base_address)) - &card_table[0]; size_t ecardw = (uint32_t*)(min(g_addresses[i]+OS_PAGE_SIZE, high_address)) - &card_table[0]; - assert (bcardw >= card_word (card_of (g_lowest_address))); + assert (bcardw >= card_word (card_of (g_gc_lowest_address))); card_bundles_set (cardw_card_bundle (bcardw), cardw_card_bundle (align_cardw_on_bundle (ecardw))); @@ -9627,7 +9671,7 @@ void gc_heap::adjust_ephemeral_limits (bool is_runtime_suspended) (size_t)ephemeral_low, (size_t)ephemeral_high)) // This updates the write barrier helpers with the new info. - StompWriteBarrierEphemeral(is_runtime_suspended); + stomp_write_barrier_ephemeral(is_runtime_suspended, ephemeral_low, ephemeral_high); } #if defined(TRACE_GC) || defined(GC_CONFIG_DRIVEN) @@ -9800,9 +9844,9 @@ HRESULT gc_heap::initialize_gc (size_t segment_size, settings.first_init(); - g_card_table = make_card_table (g_lowest_address, g_highest_address); + g_gc_card_table = make_card_table (g_gc_lowest_address, g_gc_highest_address); - if (!g_card_table) + if (!g_gc_card_table) return E_OUTOFMEMORY; gc_started = FALSE; @@ -10285,7 +10329,7 @@ gc_heap::init_gc_heap (int h_number) #endif //MULTIPLE_HEAPS /* todo: Need a global lock for this */ - uint32_t* ct = &g_card_table [card_word (card_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table [card_word (card_of (g_gc_lowest_address))]; own_card_table (ct); card_table = translate_card_table (ct); /* End of global lock */ @@ -10296,13 +10340,13 @@ gc_heap::init_gc_heap (int h_number) #ifdef CARD_BUNDLE card_bundle_table = translate_card_bundle_table (card_table_card_bundle_table (ct)); - assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_lowest_address))))] == + assert (&card_bundle_table [card_bundle_word (cardw_card_bundle (card_word (card_of (g_gc_lowest_address))))] == card_table_card_bundle_table (ct)); #endif //CARD_BUNDLE #ifdef MARK_ARRAY if (gc_can_use_concurrent) - mark_array = translate_mark_array (card_table_mark_array (&g_card_table[card_word (card_of (g_lowest_address))])); + mark_array = translate_mark_array (card_table_mark_array (&g_gc_card_table[card_word (card_of (g_gc_lowest_address))])); else mark_array = NULL; #endif //MARK_ARRAY @@ -15166,7 +15210,7 @@ void gc_heap::gc1() vm_heap->GcCondemnedGeneration = settings.condemned_generation; - assert (g_card_table == card_table); + assert (g_gc_card_table == card_table); { if (n == max_generation) @@ -16373,7 +16417,7 @@ int gc_heap::garbage_collect (int n) for (int i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table != g_heaps[i]->card_table) + if (g_gc_card_table != g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -16397,7 +16441,7 @@ int gc_heap::garbage_collect (int n) } #endif //BACKGROUND_GC // check for card table growth - if (g_card_table != card_table) + if (g_gc_card_table != card_table) copy_brick_card_table(); #endif //MULTIPLE_HEAPS @@ -22373,7 +22417,7 @@ void gc_heap::plan_phase (int condemned_gen_number) for (i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table!= g_heaps[i]->card_table) + if (g_gc_card_table!= g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -25079,14 +25123,14 @@ BOOL gc_heap::commit_mark_array_new_seg (gc_heap* hp, if (new_card_table == 0) { - new_card_table = g_card_table; + new_card_table = g_gc_card_table; } if (hp->card_table != new_card_table) { if (new_lowest_address == 0) { - new_lowest_address = g_lowest_address; + new_lowest_address = g_gc_lowest_address; } uint32_t* ct = &new_card_table[card_word (gcard_of (new_lowest_address))]; @@ -29076,7 +29120,7 @@ generation* gc_heap::expand_heap (int condemned_generation, return consing_gen; //copy the card and brick tables - if (g_card_table!= card_table) + if (g_gc_card_table!= card_table) copy_brick_card_table(); BOOL new_segment_p = (heap_segment_next (new_seg) == 0); @@ -32386,7 +32430,7 @@ void gc_heap::clear_all_mark_array() void gc_heap::verify_mark_array_cleared (heap_segment* seg) { #if defined (VERIFY_HEAP) && defined (MARK_ARRAY) - assert (card_table == g_card_table); + assert (card_table == g_gc_card_table); size_t markw = mark_word_of (heap_segment_mem (seg)); size_t markw_end = mark_word_of (heap_segment_reserved (seg)); @@ -32734,8 +32778,8 @@ gc_heap::verify_heap (BOOL begin_gc_p) #endif //BACKGROUND_GC #ifndef MULTIPLE_HEAPS - if ((g_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) || - (g_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment))) + if ((g_gc_ephemeral_low != generation_allocation_start (generation_of (max_generation - 1))) || + (g_gc_ephemeral_high != heap_segment_reserved (ephemeral_heap_segment))) { FATAL_GC_ERROR(); } @@ -32794,7 +32838,7 @@ gc_heap::verify_heap (BOOL begin_gc_p) for (int i = 0; i < n_heaps; i++) { //copy the card and brick tables - if (g_card_table != g_heaps[i]->card_table) + if (g_gc_card_table != g_heaps[i]->card_table) { g_heaps[i]->copy_brick_card_table(); } @@ -32803,7 +32847,7 @@ gc_heap::verify_heap (BOOL begin_gc_p) current_join->restart(); } #else - if (g_card_table != card_table) + if (g_gc_card_table != card_table) copy_brick_card_table(); #endif //MULTIPLE_HEAPS @@ -33228,11 +33272,11 @@ HRESULT GCHeap::Shutdown () //CloseHandle (WaitForGCEvent); //find out if the global card table hasn't been used yet - uint32_t* ct = &g_card_table[card_word (gcard_of (g_lowest_address))]; + uint32_t* ct = &g_gc_card_table[card_word (gcard_of (g_gc_lowest_address))]; if (card_table_refcount (ct) == 0) { destroy_card_table (ct); - g_card_table = 0; + g_gc_card_table = 0; #ifdef FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP SoftwareWriteWatch::StaticClose(); #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP @@ -33392,7 +33436,7 @@ HRESULT GCHeap::Initialize () return E_FAIL; } - StompWriteBarrierResize(true, false); + stomp_write_barrier_initialize(); #ifndef FEATURE_REDHAWK // Redhawk forces relocation a different way #if defined (STRESS_HEAP) && !defined (MULTIPLE_HEAPS) @@ -33513,7 +33557,7 @@ Object * GCHeap::NextObj (Object * object) uint8_t* o = (uint8_t*)object; #ifndef FEATURE_BASICFREEZE - if (!((o < g_highest_address) && (o >= g_lowest_address))) + if (!((o < g_gc_highest_address) && (o >= g_gc_lowest_address))) { return NULL; } @@ -33584,7 +33628,7 @@ BOOL GCHeap::IsHeapPointer (void* vpObject, BOOL small_heap_only) uint8_t* object = (uint8_t*) vpObject; #ifndef FEATURE_BASICFREEZE - if (!((object < g_highest_address) && (object >= g_lowest_address))) + if (!((object < g_gc_highest_address) && (object >= g_gc_lowest_address))) return FALSE; #endif //!FEATURE_BASICFREEZE @@ -35695,12 +35739,12 @@ GCHeap::SetCardsAfterBulkCopy( Object **StartPoint, size_t len ) end = StartPoint + (len/sizeof(Object*)); while (rover < end) { - if ( (((uint8_t*)*rover) >= g_ephemeral_low) && (((uint8_t*)*rover) < g_ephemeral_high) ) + 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_card_table[card/card_word_width], (1U << (card % card_word_width))); + 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)); } @@ -36568,7 +36612,7 @@ void initGCShadow() if (!(g_pConfig->GetHeapVerifyLevel() & EEConfig::HEAPVERIFY_BARRIERCHECK)) return; - size_t len = g_highest_address - g_lowest_address; + size_t len = g_gc_highest_address - g_gc_lowest_address; if (len > (size_t)(g_GCShadowEnd - g_GCShadow)) { deleteGCShadow(); @@ -36587,10 +36631,10 @@ void initGCShadow() g_GCShadowEnd += len; } - // save the value of g_lowest_address at this time. If this value changes before + // save the value of g_gc_lowest_address at this time. If this value changes before // the next call to checkGCWriteBarrier() it means we extended the heap (with a // large object segment most probably), and the whole shadow segment is inconsistent. - g_shadow_lowest_address = g_lowest_address; + g_shadow_lowest_address = g_gc_lowest_address; //****** Copy the whole GC heap ****** // @@ -36600,7 +36644,7 @@ void initGCShadow() generation* gen = gc_heap::generation_of (max_generation); heap_segment* seg = heap_segment_rw (generation_start_segment (gen)); - ptrdiff_t delta = g_GCShadow - g_lowest_address; + ptrdiff_t delta = g_GCShadow - g_gc_lowest_address; BOOL small_object_segments = TRUE; while(1) { @@ -36628,7 +36672,7 @@ void initGCShadow() // test to see if 'ptr' was only updated via the write barrier. inline void testGCShadow(Object** ptr) { - Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_lowest_address)]; + Object** shadow = (Object**) &g_GCShadow[((uint8_t*) ptr - g_gc_lowest_address)]; if (*ptr != 0 && (uint8_t*) shadow < g_GCShadowEnd && *ptr != *shadow) { @@ -36687,9 +36731,9 @@ void testGCShadowHelper (uint8_t* x) // Walk the whole heap, looking for pointers that were not updated with the write barrier. void checkGCWriteBarrier() { - // g_shadow_lowest_address != g_lowest_address means the GC heap was extended by a segment + // g_shadow_lowest_address != g_gc_lowest_address means the GC heap was extended by a segment // and the GC shadow segment did not track that change! - if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_lowest_address) + if (g_GCShadowEnd <= g_GCShadow || g_shadow_lowest_address != g_gc_lowest_address) { // No shadow stack, nothing to check. return; diff --git a/src/coreclr/src/gc/gc.h b/src/coreclr/src/gc/gc.h index da047ad..b7f1e95 100644 --- a/src/coreclr/src/gc/gc.h +++ b/src/coreclr/src/gc/gc.h @@ -137,6 +137,12 @@ class DacHeapWalker; #define MP_LOCKS +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(); class GCHeap; diff --git a/src/coreclr/src/gc/gccommon.cpp b/src/coreclr/src/gc/gccommon.cpp index 2e6bfce..d1ccddd 100644 --- a/src/coreclr/src/gc/gccommon.cpp +++ b/src/coreclr/src/gc/gccommon.cpp @@ -26,28 +26,24 @@ IGCHeapInternal* g_theGCHeap; IGCToCLR* g_theGCToCLR; #endif // FEATURE_STANDALONE_GC -/* global versions of the card table and brick table */ -GPTR_IMPL(uint32_t,g_card_table); - -/* absolute bounds of the GC memory */ -GPTR_IMPL_INIT(uint8_t,g_lowest_address,0); -GPTR_IMPL_INIT(uint8_t,g_highest_address,0); - #ifdef GC_CONFIG_DRIVEN GARY_IMPL(size_t, gc_global_mechanisms, MAX_GLOBAL_GC_MECHANISMS_COUNT); #endif //GC_CONFIG_DRIVEN #ifndef DACCESS_COMPILE -uint8_t* g_ephemeral_low = (uint8_t*)1; -uint8_t* g_ephemeral_high = (uint8_t*)~0; - #ifdef WRITE_BARRIER_CHECK uint8_t* g_GCShadow; uint8_t* g_GCShadowEnd; uint8_t* g_shadow_lowest_address = NULL; #endif +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; #ifdef GC_CONFIG_DRIVEN diff --git a/src/coreclr/src/gc/gcenv.ee.standalone.inl b/src/coreclr/src/gc/gcenv.ee.standalone.inl index a4f86e1..3b64586 100644 --- a/src/coreclr/src/gc/gcenv.ee.standalone.inl +++ b/src/coreclr/src/gc/gcenv.ee.standalone.inl @@ -166,4 +166,11 @@ inline void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) assert(g_theGCToCLR != nullptr); return g_theGCToCLR->DiagWalkBGCSurvivors(gcContext); } + +inline void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ + assert(g_theGCToCLR != nullptr); + g_theGCToCLR->StompWriteBarrier(args); +} + #endif // __GCTOENV_EE_STANDALONE_INL__ diff --git a/src/coreclr/src/gc/gcinterface.ee.h b/src/coreclr/src/gc/gcinterface.ee.h index 3e6631b..c5f87ef 100644 --- a/src/coreclr/src/gc/gcinterface.ee.h +++ b/src/coreclr/src/gc/gcinterface.ee.h @@ -123,6 +123,11 @@ public: // At the end of a background GC, gives the diagnostics code a chance to run. virtual void DiagWalkBGCSurvivors(void* gcContext) = 0; + + // Informs the EE of changes to the location of the card table, potentially updating the write + // barrier if it needs to be updated. + virtual + void StompWriteBarrier(WriteBarrierParameters* args) = 0; }; #endif // _GCINTERFACE_EE_H_ diff --git a/src/coreclr/src/gc/gcinterface.h b/src/coreclr/src/gc/gcinterface.h index 1f66809..1457848 100644 --- a/src/coreclr/src/gc/gcinterface.h +++ b/src/coreclr/src/gc/gcinterface.h @@ -41,6 +41,57 @@ typedef enum walk_for_loh = 3 } walk_surv_type; +// Different operations that can be done by GCToEEInterface::StompWriteBarrier +enum class WriteBarrierOp +{ + StompResize, + StompEphemeral, + Initialize +}; + +// Arguments to GCToEEInterface::StompWriteBarrier +struct WriteBarrierParameters +{ + // The operation that StompWriteBarrier will perform. + WriteBarrierOp operation; + + // Whether or not the runtime is currently suspended. If it is not, + // the EE will need to suspend it before bashing the write barrier. + // Used for all operations. + bool is_runtime_suspended; + + // Whether or not the GC has moved the ephemeral generation to no longer + // be at the top of the heap. When the ephemeral generation is at the top + // of the heap, and the write barrier observes that a pointer is greater than + // g_ephemeral_low, it does not need to check that the pointer is less than + // g_ephemeral_high because there is nothing in the GC heap above the ephemeral + // generation. When this is not the case, however, the GC must inform the EE + // so that the EE can switch to a write barrier that checks that a pointer + // is both greater than g_ephemeral_low and less than g_ephemeral_high. + // Used for WriteBarrierOp::StompResize. + bool requires_upper_bounds_check; + + // The new card table location. May or may not be the same as the previous + // card table. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint32_t* card_table; + + // The heap's new low boundary. May or may not be the same as the previous + // value. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint8_t* lowest_address; + + // The heap's new high boundary. May or may not be the same as the previous + // value. Used for WriteBarrierOp::Initialize and WriteBarrierOp::StompResize. + uint8_t* highest_address; + + // The new start of the ephemeral generation. + // Used for WriteBarrierOp::StompEphemeral. + uint8_t* ephemeral_lo; + + // The new end of the ephemeral generation. + // Used for WriteBarrierOp::StompEphemeral. + uint8_t* ephemeral_hi; +}; + #include "gcinterface.ee.h" // The allocation context must be known to the VM for use in the allocation @@ -110,19 +161,6 @@ IGCHeap* InitializeGarbageCollector(IGCToCLR* clrToGC); // and the heap is actually recated. void InitializeHeapType(bool bServerHeap); -#ifndef DACCESS_COMPILE -extern "C" { -#endif // !DACCESS_COMPILE -GPTR_DECL(uint8_t,g_lowest_address); -GPTR_DECL(uint8_t,g_highest_address); -GPTR_DECL(uint32_t,g_card_table); -#ifndef DACCESS_COMPILE -} -#endif // !DACCESS_COMPILE - -extern "C" uint8_t* g_ephemeral_low; -extern "C" uint8_t* g_ephemeral_high; - #ifdef WRITE_BARRIER_CHECK //always defined, but should be 0 in Server GC extern uint8_t* g_GCShadow; diff --git a/src/coreclr/src/gc/gcpriv.h b/src/coreclr/src/gc/gcpriv.h index c088c34..3bed8c2 100644 --- a/src/coreclr/src/gc/gcpriv.h +++ b/src/coreclr/src/gc/gcpriv.h @@ -4319,9 +4319,6 @@ dynamic_data* gc_heap::dynamic_data_of (int gen_number) return &dynamic_data_table [ gen_number ]; } -extern "C" uint8_t* g_ephemeral_low; -extern "C" uint8_t* g_ephemeral_high; - #define card_word_width ((size_t)32) // diff --git a/src/coreclr/src/gc/sample/GCSample.cpp b/src/coreclr/src/gc/sample/GCSample.cpp index 7f0dd8c..664dc38 100644 --- a/src/coreclr/src/gc/sample/GCSample.cpp +++ b/src/coreclr/src/gc/sample/GCSample.cpp @@ -91,14 +91,14 @@ inline void ErectWriteBarrier(Object ** dst, Object * ref) { // if the dst is outside of the heap (unboxed value classes) then we // simply exit - if (((uint8_t*)dst < g_lowest_address) || ((uint8_t*)dst >= g_highest_address)) + if (((uint8_t*)dst < g_gc_lowest_address) || ((uint8_t*)dst >= g_gc_highest_address)) return; - if((uint8_t*)ref >= g_ephemeral_low && (uint8_t*)ref < g_ephemeral_high) + 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_card_table) + card_byte((uint8_t *)dst); + uint8_t* pCardByte = (uint8_t *)*(volatile uint8_t **)(&g_gc_card_table) + card_byte((uint8_t *)dst); if(*pCardByte != 0xFF) *pCardByte = 0xFF; } diff --git a/src/coreclr/src/gc/sample/gcenv.ee.cpp b/src/coreclr/src/gc/sample/gcenv.ee.cpp index 779d034..51a735c 100644 --- a/src/coreclr/src/gc/sample/gcenv.ee.cpp +++ b/src/coreclr/src/gc/sample/gcenv.ee.cpp @@ -249,6 +249,10 @@ void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) { } +void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ +} + void FinalizerThread::EnableFinalization() { // Signal to finalizer thread that there are objects to finalize @@ -266,14 +270,6 @@ bool IsGCSpecialThread() return false; } -void StompWriteBarrierEphemeral(bool /* isRuntimeSuspended */) -{ -} - -void StompWriteBarrierResize(bool /* isRuntimeSuspended */, bool /*bReqUpperBoundsCheck*/) -{ -} - bool IsGCThread() { return false; diff --git a/src/coreclr/src/vm/gcenv.ee.cpp b/src/coreclr/src/vm/gcenv.ee.cpp index 604ac29..3be8384 100644 --- a/src/coreclr/src/vm/gcenv.ee.cpp +++ b/src/coreclr/src/vm/gcenv.ee.cpp @@ -1214,3 +1214,57 @@ void GCToEEInterface::DiagWalkBGCSurvivors(void* gcContext) #endif //GC_PROFILING || FEATURE_EVENT_TRACE } +void GCToEEInterface::StompWriteBarrier(WriteBarrierParameters* args) +{ + assert(args != nullptr); + switch (args->operation) + { + case WriteBarrierOp::StompResize: + // StompResize requires a new card table, a new lowest address, and + // a new highest address + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + g_card_table = args->card_table; + ::StompWriteBarrierResize(args->is_runtime_suspended, args->requires_upper_bounds_check); + + // We need to make sure that other threads executing checked write barriers + // will see the g_card_table update before g_lowest/highest_address updates. + // Otherwise, the checked write barrier may AV accessing the old card table + // with address that it does not cover. Write barriers access card table + // without memory barriers for performance reasons, so we need to flush + // the store buffers here. + FlushProcessWriteBuffers(); + + g_lowest_address = args->lowest_address; + VolatileStore(&g_highest_address, args->highest_address); + return; + case WriteBarrierOp::StompEphemeral: + // StompEphemeral requires a new ephemeral low and a new ephemeral high + assert(args->ephemeral_lo != nullptr); + assert(args->ephemeral_hi != nullptr); + g_ephemeral_low = args->ephemeral_lo; + g_ephemeral_high = args->ephemeral_hi; + ::StompWriteBarrierEphemeral(args->is_runtime_suspended); + return; + case WriteBarrierOp::Initialize: + // This operation should only be invoked once, upon initialization. + assert(g_card_table == nullptr); + assert(g_lowest_address == nullptr); + assert(g_highest_address == nullptr); + assert(args->card_table != nullptr); + assert(args->lowest_address != nullptr); + assert(args->highest_address != nullptr); + assert(args->is_runtime_suspended && "the runtime must be suspended here!"); + assert(!args->requires_upper_bounds_check && "the ephemeral generation must be at the top of the heap!"); + + g_card_table = args->card_table; + FlushProcessWriteBuffers(); + g_lowest_address = args->lowest_address; + VolatileStore(&g_highest_address, args->highest_address); + ::StompWriteBarrierResize(true, false); + return; + default: + assert(!"unknown WriteBarrierOp enum"); + } +} diff --git a/src/coreclr/src/vm/gcenv.ee.h b/src/coreclr/src/vm/gcenv.ee.h index 7511a7c..f431221 100644 --- a/src/coreclr/src/vm/gcenv.ee.h +++ b/src/coreclr/src/vm/gcenv.ee.h @@ -41,6 +41,7 @@ public: void DiagWalkSurvivors(void* gcContext); void DiagWalkLOHSurvivors(void* gcContext); void DiagWalkBGCSurvivors(void* gcContext); + void StompWriteBarrier(WriteBarrierParameters* args); }; #endif // FEATURE_STANDALONE_GC diff --git a/src/coreclr/src/vm/gcheaputilities.cpp b/src/coreclr/src/vm/gcheaputilities.cpp index ceadb9e..ac24fa3 100644 --- a/src/coreclr/src/vm/gcheaputilities.cpp +++ b/src/coreclr/src/vm/gcheaputilities.cpp @@ -5,5 +5,15 @@ #include "common.h" #include "gcheaputilities.h" +// These globals are variables used within the GC and maintained +// by the EE for use in write barriers. It is the responsibility +// of the GC to communicate updates to these globals to the EE through +// GCToEEInterface::StompWriteBarrierResize and GCToEEInterface::StompWriteBarrierEphemeral. +GPTR_IMPL_INIT(uint32_t, g_card_table, nullptr); +GPTR_IMPL_INIT(uint8_t, g_lowest_address, nullptr); +GPTR_IMPL_INIT(uint8_t, g_highest_address, nullptr); +uint8_t* g_ephemeral_low = (uint8_t*)1; +uint8_t* g_ephemeral_high = (uint8_t*)~0; + // This is the global GC heap, maintained by the VM. GPTR_IMPL(IGCHeap, g_pGCHeap); \ No newline at end of file diff --git a/src/coreclr/src/vm/gcheaputilities.h b/src/coreclr/src/vm/gcheaputilities.h index e8f6f0a..e5883fc 100644 --- a/src/coreclr/src/vm/gcheaputilities.h +++ b/src/coreclr/src/vm/gcheaputilities.h @@ -113,4 +113,17 @@ private: GCHeapUtilities() = delete; }; +#ifndef DACCESS_COMPILE +extern "C" { +#endif // !DACCESS_COMPILE +GPTR_DECL(uint8_t,g_lowest_address); +GPTR_DECL(uint8_t,g_highest_address); +GPTR_DECL(uint32_t,g_card_table); +#ifndef DACCESS_COMPILE +} +#endif // !DACCESS_COMPILE + +extern "C" uint8_t* g_ephemeral_low; +extern "C" uint8_t* g_ephemeral_high; + #endif // _GCHEAPUTILITIES_H_ \ No newline at end of file -- 2.7.4