From 775053c21b33f08fb65d882a7587a98a75bf6c19 Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Mon, 31 Mar 2025 17:16:14 +0300 Subject: [PATCH 01/11] Fix arm32 build with USE_REGIONS enabled --- src/coreclr/gc/gc.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index dd2c7e4fbf1c14..08c9fbcfedbbc7 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -2448,9 +2448,9 @@ size_t gc_heap::hc_change_cancelled_count_bgc = 0; #endif //BACKGROUND_GC #endif //DYNAMIC_HEAP_COUNT -#if defined(HOST_64BIT) #define MAX_ALLOWED_MEM_LOAD 85 +#if defined(HOST_64BIT) // consider putting this in dynamic data - // we may want different values for workstation // and server GC. @@ -25365,7 +25365,14 @@ void gc_heap::equalize_promoted_bytes(int condemned_gen_number) // check that the fields of a decommissioned heap have their expected values, // i.e. were not inadvertently modified +#ifdef HOST_64BIT #define DECOMMISSIONED_VALUE 0xdec0dec0dec0dec0 +static const ptrdiff_t UNINITIALIZED_VALUE = 0xbaadbaadbaadbaad; +#else // HOST_32BIT +#define DECOMMISSIONED_VALUE 0xdec0dec0 +static const ptrdiff_t UNINITIALIZED_VALUE = 0xbaadbaad; +#endif // HOST_64BIT + static const size_t DECOMMISSIONED_SIZE_T = DECOMMISSIONED_VALUE; static const ptrdiff_t DECOMMISSIONED_PTRDIFF_T = (ptrdiff_t)DECOMMISSIONED_VALUE; static const ptrdiff_t DECOMMISSIONED_UINT64_T = (uint64_t)DECOMMISSIONED_VALUE; @@ -25377,8 +25384,6 @@ static const BOOL DECOMMISSIONED_BOOL = 0xdec0dec0; static const BOOL DECOMMISSIONED_INT = (int)0xdec0dec0; static const float DECOMMISSIONED_FLOAT = (float)DECOMMISSIONED_VALUE; -static const ptrdiff_t UNINITIALIZED_VALUE = 0xbaadbaadbaadbaad; - void gc_heap::check_decommissioned_heap() { // keep the mark stack for the time being From 6ae12e85fe36936012f988d72c4f42a797d16c3e Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Thu, 17 Apr 2025 12:57:02 +0300 Subject: [PATCH 02/11] Fix heap_segment_allocated < 0 for 32bit REGIONS --- src/coreclr/gc/gc.cpp | 62 +++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 08c9fbcfedbbc7..1cbd9b3588c1d3 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -3819,23 +3819,7 @@ size_t get_basic_region_index_for_address (uint8_t* address) return (skewed_basic_region_index - get_skewed_basic_region_index_for_address (g_gc_lowest_address)); } -// Go from a random address to its region info. The random address could be -// in one of the basic regions of a larger region so we need to check for that. -inline -heap_segment* get_region_info_for_address (uint8_t* address) -{ - size_t basic_region_index = (size_t)address >> gc_heap::min_segment_size_shr; - heap_segment* basic_region_info_entry = (heap_segment*)&seg_mapping_table[basic_region_index]; - ptrdiff_t first_field = (ptrdiff_t)heap_segment_allocated (basic_region_info_entry); - if (first_field < 0) - { - basic_region_index += first_field; - } - - return ((heap_segment*)(&seg_mapping_table[basic_region_index])); -} - -// Go from the physical start of a region to its region info. +// Go from region start to its region info. inline heap_segment* get_region_info (uint8_t* region_start) { @@ -3854,6 +3838,18 @@ uint8_t* get_region_start (heap_segment* region_info) return (obj_start - sizeof (aligned_plug_and_gap)); } +// Go from a random address to its region info. The random address could be +// in one of the basic regions of a larger region so we need to check for that. +inline +heap_segment* get_region_info_for_address (uint8_t* address) +{ + size_t basic_region_index = (size_t)address >> gc_heap::min_segment_size_shr; + heap_segment* basic_region_info_entry = (heap_segment*)&seg_mapping_table[basic_region_index]; + uint8_t* region_start = get_region_start(basic_region_info_entry); + + return get_region_info(region_start); +} + inline size_t get_region_size (heap_segment* region_info) { @@ -4635,30 +4631,25 @@ heap_segment* seg_mapping_table_segment_of (uint8_t* o) return ro_segment_lookup (o); #endif //FEATURE_BASICFREEZE - size_t index = (size_t)o >> gc_heap::min_segment_size_shr; - seg_mapping* entry = &seg_mapping_table[index]; - #ifdef USE_REGIONS // REGIONS TODO: I think we could simplify this to having the same info for each // basic entry in a large region so we can get it right away instead of having to go // back some entries. - ptrdiff_t first_field = (ptrdiff_t)heap_segment_allocated ((heap_segment*)entry); - if (first_field == 0) + heap_segment* seg = get_region_info_for_address(o); + + uint8_t* allocated = heap_segment_allocated (seg); + if (allocated == NULL) { dprintf (REGIONS_LOG, ("asked for seg for %p, in a freed region mem: %p, committed %p", - o, heap_segment_mem ((heap_segment*)entry), - heap_segment_committed ((heap_segment*)entry))); + o, heap_segment_mem (seg), heap_segment_committed (seg))); return 0; } // Regions are never going to intersect an ro seg, so this can never be ro_in_entry. - assert (first_field != 0); - assert (first_field != ro_in_entry); - if (first_field < 0) - { - index += first_field; - } - heap_segment* seg = (heap_segment*)&seg_mapping_table[index]; + assert ((ptrdiff_t)allocated != ro_in_entry); #else //USE_REGIONS + size_t index = (size_t)o >> gc_heap::min_segment_size_shr; + seg_mapping* entry = &seg_mapping_table[index]; + dprintf (2, ("checking obj %p, index is %zd, entry: boundary: %p, seg0: %p, seg1: %p", o, index, (entry->boundary + 1), (uint8_t*)(entry->seg0), (uint8_t*)(entry->seg1))); @@ -12462,19 +12453,20 @@ void gc_heap::init_heap_segment (heap_segment* seg, gc_heap* hp set_region_gen_num (seg, gen_num_for_region); heap_segment_plan_gen_num (seg) = gen_num_for_region; heap_segment_swept_in_plan (seg) = false; -#endif //USE_REGIONS -#ifdef USE_REGIONS int num_basic_regions = (int)(size >> min_segment_size_shr); + assert(size == (size_t) num_basic_regions << min_segment_size_shr); size_t basic_region_size = (size_t)1 << min_segment_size_shr; dprintf (REGIONS_LOG, ("this region contains %d basic regions", num_basic_regions)); - if (num_basic_regions > 1) + // For large region get_region_info(start) equals to seg only for first segment. + if (num_basic_regions > 1 && seg == get_region_info(start)) { for (int i = 1; i < num_basic_regions; i++) { uint8_t* basic_region_start = start + (i * basic_region_size); heap_segment* basic_region = get_region_info (basic_region_start); - heap_segment_allocated (basic_region) = (uint8_t*)(ptrdiff_t)-i; + heap_segment_mem (basic_region) = heap_segment_mem (seg); + dprintf (REGIONS_LOG, ("Initing basic region %p->%p(%zdmb) alloc to %p", basic_region_start, (basic_region_start + basic_region_size), (size_t)(basic_region_size / 1024 / 1024), From a21216873255753cdabd4f2992154158a6edc9b3 Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Mon, 28 Apr 2025 16:26:17 +0300 Subject: [PATCH 03/11] USE_REGIONS 32bit fix sigsegv GC/Features/HeapExpansion/plug/plug.dll --- src/coreclr/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 1cbd9b3588c1d3..155d790c647256 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -46883,7 +46883,7 @@ void gc_heap::background_sweep() else { // For SOH segments we go backwards. - next_seg = heap_segment_prev (gen_start_seg, seg); + next_seg = heap_segment_prev (start_seg, seg); } #endif //DOUBLY_LINKED_FL From 1c3475ae71f50d841107734988d0c45e3c33ffb9 Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Mon, 12 May 2025 20:49:58 +0300 Subject: [PATCH 04/11] Fix ClearMarked() for not DOUBLY_LINKED_FL case --- src/coreclr/gc/gc.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 155d790c647256..73a95c952e5cdf 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -4848,11 +4848,7 @@ class CObjectHeader : public Object // Now we set more bits should actually only clear the mark bit void ClearMarked() { -#ifdef DOUBLY_LINKED_FL RawSetMethodTable ((MethodTable *)(((size_t) RawGetMethodTable()) & (~GC_MARKED))); -#else - RawSetMethodTable (GetMethodTable()); -#endif //DOUBLY_LINKED_FL } #ifdef DOUBLY_LINKED_FL From 366b8797c5198f57f918b25db80cf8d46f277356 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Mon, 19 May 2025 13:19:30 +0300 Subject: [PATCH 05/11] Account for empty mark_array during calculation of memory to commit for seg_mapping_table --- src/coreclr/gc/gc.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 73a95c952e5cdf..3a7016685894a2 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -9418,7 +9418,16 @@ bool gc_heap::get_card_table_commit_layout (uint8_t* from, uint8_t* to, assert (required_begin <= required_end); commit_end = align_on_page(required_end); - commit_end = min (commit_end, align_lower_page(bookkeeping_start + card_table_element_layout[i + 1])); + // Account for seg_mapping_table ending on the same page as mark_array begins. + // If background GC is disabled in run time (i.e. empty mark_array with 0 size), no need to align down. + if (i != seg_mapping_table_element +#ifdef BACKGROUND_GC + || card_table_element_layout[mark_array_element] != card_table_element_layout[total_bookkeeping_elements] +#endif // BACKGROUND_GC + ) + { + commit_end = min (commit_end, align_lower_page(bookkeeping_start + card_table_element_layout[i + 1])); + } commit_begin = min (commit_begin, commit_end); assert (commit_begin <= commit_end); From 8feaf6d588bad679e2c804afed1985e2b0b5b8da Mon Sep 17 00:00:00 2001 From: Timur Mustafin Date: Mon, 19 May 2025 16:16:31 +0300 Subject: [PATCH 06/11] Fix regions_range overflow for 32-bit --- src/coreclr/gc/gc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 3a7016685894a2..2d49f48db5ee81 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -49239,8 +49239,14 @@ HRESULT GCHeap::Initialize() } else { +#ifdef HOST_64BIT // If no hard_limit is configured the reservation size is min of 1/2 GetVirtualMemoryLimit() or max of 256Gb or 2x physical limit. gc_heap::regions_range = max((size_t)256 * 1024 * 1024 * 1024, (size_t)(2 * gc_heap::total_physical_mem)); +#else + gc_heap::regions_range = (2 * gc_heap::total_physical_mem) <= SIZE_MAX ? + (size_t)(2 * gc_heap::total_physical_mem) : + (size_t) gc_heap::total_physical_mem; +#endif } size_t virtual_mem_limit = GCToOSInterface::GetVirtualMemoryLimit(); gc_heap::regions_range = min(gc_heap::regions_range, virtual_mem_limit/2); From 76e13c3bb79094b85758fb665f6a7afb902a4503 Mon Sep 17 00:00:00 2001 From: ashaurtaev Date: Thu, 22 May 2025 11:01:12 +0300 Subject: [PATCH 07/11] Fix assert for prepare_for_no_gc_region --- src/coreclr/gc/gc.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 2d49f48db5ee81..389a8c1942dd74 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -23549,7 +23549,16 @@ start_no_gc_region_status gc_heap::prepare_for_no_gc_region (uint64_t total_size // 4GB of RAM. Once Local GC code divergence is resolved and code is flowing // more freely between branches, it would be good to clean this up to use // total_physical_mem instead of SIZE_T_MAX. - assert(total_allowed_soh_allocation <= SIZE_T_MAX); + + // Clamp total allowed SOH allocation to SIZE_T_MAX to prevent overflow in 32-bit adress spaces. + // On 32-bit architectures max_soh_allocated * num_heaps can exceed 32-bit address space, + // while on 64-bit architectures this prevents threoretical overflow when using an large + // number of heaps (num_heaps > 1). This ensures all size calculations remain within the maximum + // representable value for the platform's address space. + if(total_allowed_soh_allocation > SIZE_T_MAX) + { + total_allowed_soh_allocation = SIZE_T_MAX; + } uint64_t total_allowed_loh_allocation = SIZE_T_MAX; uint64_t total_allowed_soh_alloc_scaled = allocation_no_gc_soh > 0 ? static_cast(total_allowed_soh_allocation / scale_factor) : 0; uint64_t total_allowed_loh_alloc_scaled = allocation_no_gc_loh > 0 ? static_cast(total_allowed_loh_allocation / scale_factor) : 0; From 34716869f70994e34574a3d7c3269435c418c5f7 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Fri, 23 May 2025 13:31:21 +0300 Subject: [PATCH 08/11] Fix search of prev segment --- src/coreclr/gc/gc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 389a8c1942dd74..21dcaaf86d6243 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -46897,7 +46897,7 @@ void gc_heap::background_sweep() else { // For SOH segments we go backwards. - next_seg = heap_segment_prev (start_seg, seg); + next_seg = heap_segment_prev (gen_start_seg, seg); } #endif //DOUBLY_LINKED_FL From f41c95b811d4645866f648ff988e115199fbfa9c Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Fri, 23 May 2025 17:20:38 +0300 Subject: [PATCH 09/11] Account for empty next_seg --- src/coreclr/gc/gc.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 21dcaaf86d6243..4505fc12d575b1 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -46136,7 +46136,10 @@ void gc_heap::generation_delete_heap_segment (generation* gen, // For doubly linked list we go forward for SOH heap_segment_next (prev_seg) = next_seg; #else //DOUBLY_LINKED_FL - heap_segment_next (next_seg) = prev_seg; + if (next_seg) + { + heap_segment_next (next_seg) = prev_seg; + } #endif //DOUBLY_LINKED_FL dprintf (3, ("Preparing empty small segment %zx for deletion", (size_t)seg)); From f30ed91351b5d4d6b25d7fef69eac71030da0bd6 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Sat, 24 May 2025 16:34:06 +0300 Subject: [PATCH 10/11] Limit reserved range with 1 Gb on 32bit --- src/coreclr/gc/gc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 4505fc12d575b1..15c5425dc51fd2 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -49261,7 +49261,11 @@ HRESULT GCHeap::Initialize() #endif } size_t virtual_mem_limit = GCToOSInterface::GetVirtualMemoryLimit(); +#ifdef HOST_64BIT gc_heap::regions_range = min(gc_heap::regions_range, virtual_mem_limit/2); +#else + gc_heap::regions_range = min(gc_heap::regions_range, virtual_mem_limit/4); +#endif gc_heap::regions_range = align_on_page(gc_heap::regions_range); } GCConfig::SetGCRegionRange(gc_heap::regions_range); From fdaf37c1609e4ef801596bdb2da9978e9914f073 Mon Sep 17 00:00:00 2001 From: Gleb Balykov Date: Mon, 2 Jun 2025 14:58:38 +0300 Subject: [PATCH 11/11] Disable DATAS for 32bit for now --- src/coreclr/gc/gc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/coreclr/gc/gc.cpp b/src/coreclr/gc/gc.cpp index 15c5425dc51fd2..0b5a2032c2f7cb 100644 --- a/src/coreclr/gc/gc.cpp +++ b/src/coreclr/gc/gc.cpp @@ -14453,6 +14453,10 @@ HRESULT gc_heap::initialize_gc (size_t soh_segment_size, { dynamic_adaptation_mode = 0; } +#if !defined(HOST_64BIT) + // TODO: remove this when DATAS is ready for 32 bit + dynamic_adaptation_mode = 0; +#endif if ((dynamic_adaptation_mode == dynamic_adaptation_to_application_sizes) && (conserve_mem_setting == 0)) conserve_mem_setting = 5;