@@ -926,36 +926,36 @@ heap_page_in_global_empty_pages_pool(rb_objspace_t *objspace, struct heap_page *
926926#define GET_HEAP_WB_UNPROTECTED_BITS (x ) (&GET_HEAP_PAGE(x)->wb_unprotected_bits[0])
927927#define GET_HEAP_MARKING_BITS (x ) (&GET_HEAP_PAGE(x)->marking_bits[0])
928928
929- /* Planar age_bits layout: two bit-planes, each with the same 1-bit-per-slot
930- * layout as mark_bits. age_bits[i*2] holds bit 0, age_bits[i*2+1] holds
931- * bit 1 of the age for the slots covered by mark_bits[i]. */
929+ // Planar layout: age_bits[i*2] = plane 0 (low bit), age_bits[i*2+1] = plane 1 (high bit)
930+ // This matches the mark_bits layout so bulk clearing with ~unmarked works directly.
931+ #define RVALUE_AGE_BITMAP_PLANE_INDEX (n ) BITMAP_INDEX(n)
932+ #define RVALUE_AGE_BITMAP_OFFSET (n ) BITMAP_OFFSET(n)
932933
933934static int
934935RVALUE_AGE_GET (VALUE obj )
935936{
936937 bits_t * age_bits = GET_HEAP_PAGE (obj )-> age_bits ;
937- int idx = BITMAP_INDEX (obj ) * 2 ;
938- int shift = BITMAP_OFFSET (obj );
939- int bit0 = (age_bits [idx ] >> shift ) & 1 ;
940- int bit1 = (age_bits [idx + 1 ] >> shift ) & 1 ;
941- return bit0 | (bit1 << 1 );
938+ int idx = RVALUE_AGE_BITMAP_PLANE_INDEX (obj );
939+ int offset = RVALUE_AGE_BITMAP_OFFSET (obj );
940+ int lo = (age_bits [idx * 2 ] >> offset ) & 1 ;
941+ int hi = (age_bits [idx * 2 + 1 ] >> offset ) & 1 ;
942+ return lo | (hi << 1 );
942943}
943944
944945static void
945946RVALUE_AGE_SET_BITMAP (VALUE obj , int age )
946947{
947948 RUBY_ASSERT (age <= RVALUE_OLD_AGE );
948949 bits_t * age_bits = GET_HEAP_PAGE (obj )-> age_bits ;
949- int idx = BITMAP_INDEX (obj ) * 2 ;
950- int shift = BITMAP_OFFSET (obj );
951- bits_t mask = (bits_t )1 << shift ;
952-
953- /* Use atomic operations because the sweep thread may concurrently clear
954- * age bits for dead objects in the same bitmap word. */
955- if (age & 1 ) { age_bits [idx ] |= mask ; }
956- else { age_bits [idx ] &= ~mask ; }
957- if (age & 2 ) { age_bits [idx + 1 ] |= mask ; }
958- else { age_bits [idx + 1 ] &= ~mask ; }
950+ int idx = RVALUE_AGE_BITMAP_PLANE_INDEX (obj );
951+ int offset = RVALUE_AGE_BITMAP_OFFSET (obj );
952+ bits_t mask = (bits_t )1 << offset ;
953+ // clear both planes
954+ age_bits [idx * 2 ] &= ~mask ;
955+ age_bits [idx * 2 + 1 ] &= ~mask ;
956+ // set the bits
957+ if (age & 1 ) age_bits [idx * 2 ] |= mask ;
958+ if (age & 2 ) age_bits [idx * 2 + 1 ] |= mask ;
959959}
960960
961961static void
@@ -2559,6 +2559,7 @@ newobj_init(VALUE klass, VALUE flags, int wb_protected, rb_objspace_t *objspace,
25592559 }
25602560#endif
25612561 GC_ASSERT (BUILTIN_TYPE (obj ) == T_NONE );
2562+ GC_ASSERT (RVALUE_AGE_GET (obj ) == 0 );
25622563 GC_ASSERT ((flags & FL_WB_PROTECTED ) == 0 );
25632564 RBASIC (obj )-> flags = flags ;
25642565 * ((VALUE * )& RBASIC (obj )-> klass ) = klass ;
@@ -3969,6 +3970,8 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit
39693970 }
39703971#endif
39713972
3973+ if (RVALUE_WB_UNPROTECTED (objspace , vp )) CLEAR_IN_BITMAP (GET_HEAP_WB_UNPROTECTED_BITS (vp ), vp );
3974+
39723975#if RGENGC_CHECK_MODE
39733976#define CHECK (x ) if (x(objspace, vp) != FALSE) rb_bug("obj_free: " #x "(%s) != FALSE", rb_obj_info(vp))
39743977 CHECK (RVALUE_WB_UNPROTECTED );
@@ -3984,6 +3987,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit
39843987 }
39853988
39863989 (void )VALGRIND_MAKE_MEM_UNDEFINED ((void * )p , BASE_SLOT_SIZE );
3990+ RVALUE_AGE_SET_BITMAP (vp , 0 );
39873991 heap_page_add_freeobj (objspace , sweep_page , vp );
39883992 gc_report (3 , objspace , "page_sweep: %s (fast path) added to freelist\n" , rb_obj_info (vp ));
39893993 ctx -> freed_slots ++ ;
@@ -3998,6 +4002,7 @@ gc_sweep_plane(rb_objspace_t *objspace, rb_heap_t *heap, uintptr_t p, bits_t bit
39984002 }
39994003 if (rb_gc_obj_free (objspace , vp )) {
40004004 (void )VALGRIND_MAKE_MEM_UNDEFINED ((void * )p , BASE_SLOT_SIZE );
4005+ RVALUE_AGE_SET_BITMAP (vp , 0 );
40014006 heap_page_add_freeobj (objspace , sweep_page , vp );
40024007 gc_report (3 , objspace , "page_sweep: %s is added to freelist\n" , rb_obj_info (vp ));
40034008 ctx -> freed_slots ++ ;
@@ -4140,8 +4145,6 @@ gc_post_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *s
41404145 bits_t slot_mask = heap -> slot_bits_mask ;
41414146
41424147 // Clear wb_unprotected and age bits for all unmarked slots.
4143- // Use atomic operations because mutator write barriers may concurrently
4144- // modify bits for live objects in the same bitmap word.
41454148 {
41464149 bits_t * wb_unprotected_bits = sweep_page -> wb_unprotected_bits ;
41474150 bits_t * age_bits = sweep_page -> age_bits ;
@@ -4204,20 +4207,6 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context
42044207
42054208 bits_t slot_mask = heap -> slot_bits_mask ;
42064209
4207- // Clear wb_unprotected and age bits for all unmarked slots.
4208- // Use atomic operations because mutator write barriers may concurrently
4209- // modify bits for live objects in the same bitmap word.
4210- {
4211- bits_t * wb_unprotected_bits = sweep_page -> wb_unprotected_bits ;
4212- bits_t * age_bits = sweep_page -> age_bits ;
4213- for (int i = 0 ; i < bitmap_plane_count ; i ++ ) {
4214- bits_t unmarked = ~bits [i ] & slot_mask ;
4215- wb_unprotected_bits [i ] &= ~unmarked ;
4216- age_bits [i * 2 ] &= ~unmarked ;
4217- age_bits [i * 2 + 1 ] &= ~unmarked ;
4218- }
4219- }
4220-
42214210 // Skip out of range slots at the head of the page
42224211 bitset = ~bits [0 ];
42234212 bitset >>= NUM_IN_PAGE (p );
0 commit comments