@@ -932,31 +932,36 @@ heap_page_in_global_empty_pages_pool(rb_objspace_t *objspace, struct heap_page *
932932#define GET_HEAP_WB_UNPROTECTED_BITS (x ) (&GET_HEAP_PAGE(x)->wb_unprotected_bits[0])
933933#define GET_HEAP_MARKING_BITS (x ) (&GET_HEAP_PAGE(x)->marking_bits[0])
934934
935- #define RVALUE_AGE_BITMAP_INDEX (n ) (NUM_IN_PAGE(n) / (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT))
936- #define RVALUE_AGE_BITMAP_OFFSET (n ) ((NUM_IN_PAGE(n) % (BITS_BITLENGTH / RVALUE_AGE_BIT_COUNT)) * RVALUE_AGE_BIT_COUNT)
935+ /* Planar age_bits layout: two bit-planes, each with the same 1-bit-per-slot
936+ * layout as mark_bits. age_bits[i*2] holds bit 0, age_bits[i*2+1] holds
937+ * bit 1 of the age for the slots covered by mark_bits[i]. */
937938
938939static int
939940RVALUE_AGE_GET (VALUE obj )
940941{
941942 bits_t * age_bits = GET_HEAP_PAGE (obj )-> age_bits ;
942- return (int )(age_bits [RVALUE_AGE_BITMAP_INDEX (obj )] >> RVALUE_AGE_BITMAP_OFFSET (obj )) & RVALUE_AGE_BIT_MASK ;
943+ int idx = BITMAP_INDEX (obj ) * 2 ;
944+ int shift = BITMAP_OFFSET (obj );
945+ int bit0 = (age_bits [idx ] >> shift ) & 1 ;
946+ int bit1 = (age_bits [idx + 1 ] >> shift ) & 1 ;
947+ return bit0 | (bit1 << 1 );
943948}
944949
945950static void
946951RVALUE_AGE_SET_BITMAP (VALUE obj , int age )
947952{
948953 RUBY_ASSERT (age <= RVALUE_OLD_AGE );
949954 bits_t * age_bits = GET_HEAP_PAGE (obj )-> age_bits ;
950- // clear the bits
951- /*age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] &= ~(RVALUE_AGE_BIT_MASK << (RVALUE_AGE_BITMAP_OFFSET( obj)));*/
952- /*// shift the correct value in*/
953- /*age_bits[RVALUE_AGE_BITMAP_INDEX(obj)] |= ((bits_t)age << RVALUE_AGE_BITMAP_OFFSET(obj));*/
954- bits_t clear_mask = RVALUE_AGE_BIT_MASK << RVALUE_AGE_BITMAP_OFFSET ( obj );
955- bits_t set_mask = ( bits_t ) age << RVALUE_AGE_BITMAP_OFFSET ( obj );
956- // clear the bits
957- RUBY_ATOMIC_VALUE_AND (age_bits [RVALUE_AGE_BITMAP_INDEX ( obj ) ], ~clear_mask );
958- // shift the correct value in
959- RUBY_ATOMIC_VALUE_OR (age_bits [RVALUE_AGE_BITMAP_INDEX ( obj ) ], set_mask );
955+ int idx = BITMAP_INDEX ( obj ) * 2 ;
956+ int shift = BITMAP_OFFSET ( obj );
957+ bits_t mask = ( bits_t ) 1 << shift ;
958+
959+ /* Use atomic operations because the sweep thread may concurrently clear
960+ * age bits for dead objects in the same bitmap word. */
961+ if ( age & 1 ) { RUBY_ATOMIC_VALUE_OR ( age_bits [ idx ], mask ); }
962+ else { RUBY_ATOMIC_VALUE_AND (age_bits [idx ], ~mask ); }
963+ if ( age & 2 ) { RUBY_ATOMIC_VALUE_OR ( age_bits [ idx + 1 ], mask ); }
964+ else { RUBY_ATOMIC_VALUE_AND (age_bits [idx + 1 ], ~ mask ); }
960965}
961966
962967static void
@@ -4104,20 +4109,6 @@ deferred_free(rb_objspace_t *objspace, VALUE obj)
41044109 return result ;
41054110}
41064111
4107- // Spread N bits into 2N bits: bit k → bits 2k and 2k+1.
4108- // e.g. 0b1010 → 0b11001100
4109- static inline bits_t
4110- spread_bits (bits_t x )
4111- {
4112- bits_t result = 0 ;
4113- for (int b = 0 ; b < BITS_BITLENGTH / 2 ; b ++ ) {
4114- if (x & ((bits_t )1 << b )) {
4115- result |= (bits_t )RVALUE_AGE_BIT_MASK << (b * RVALUE_AGE_BIT_COUNT );
4116- }
4117- }
4118- return result ;
4119- }
4120-
41214112// Clear bits for the page that was swept by the background thread.
41224113static inline void
41234114gc_post_sweep_page (rb_objspace_t * objspace , rb_heap_t * heap , struct heap_page * sweep_page , bool force_setup_mark_bits )
@@ -4163,16 +4154,8 @@ gc_post_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct heap_page *s
41634154 for (int i = 0 ; i < bitmap_plane_count ; i ++ ) {
41644155 bits_t unmarked = ~bits [i ] & slot_mask ;
41654156 RUBY_ATOMIC_VALUE_AND (wb_unprotected_bits [i ], ~unmarked );
4166- // Expand 1-bit-per-slot unmarked mask to 2-bit-per-slot age mask.
4167- // age_bits[i*2] covers the lower half of slots in mark_bits[i],
4168- // age_bits[i*2+1] covers the upper half.
4169- bits_t unmarked_lo = unmarked & (((bits_t )1 << (BITS_BITLENGTH / 2 )) - 1 );
4170- bits_t unmarked_hi = unmarked >> (BITS_BITLENGTH / 2 );
4171- // Spread each bit into 2 adjacent bits: bit N -> bits 2N and 2N+1
4172- bits_t age_mask_lo = spread_bits (unmarked_lo );
4173- bits_t age_mask_hi = spread_bits (unmarked_hi );
4174- RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 ], ~age_mask_lo );
4175- RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 + 1 ], ~age_mask_hi );
4157+ RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 ], ~unmarked );
4158+ RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 + 1 ], ~unmarked );
41764159 }
41774160 }
41784161
@@ -4245,16 +4228,8 @@ gc_sweep_page(rb_objspace_t *objspace, rb_heap_t *heap, struct gc_sweep_context
42454228 for (int i = 0 ; i < bitmap_plane_count ; i ++ ) {
42464229 bits_t unmarked = ~bits [i ] & slot_mask ;
42474230 RUBY_ATOMIC_VALUE_AND (wb_unprotected_bits [i ], ~unmarked );
4248- // Expand 1-bit-per-slot unmarked mask to 2-bit-per-slot age mask.
4249- // age_bits[i*2] covers the lower half of slots in mark_bits[i],
4250- // age_bits[i*2+1] covers the upper half.
4251- bits_t unmarked_lo = unmarked & (((bits_t )1 << (BITS_BITLENGTH / 2 )) - 1 );
4252- bits_t unmarked_hi = unmarked >> (BITS_BITLENGTH / 2 );
4253- // Spread each bit into 2 adjacent bits: bit N -> bits 2N and 2N+1
4254- bits_t age_mask_lo = spread_bits (unmarked_lo );
4255- bits_t age_mask_hi = spread_bits (unmarked_hi );
4256- RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 ], ~age_mask_lo );
4257- RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 + 1 ], ~age_mask_hi );
4231+ RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 ], ~unmarked );
4232+ RUBY_ATOMIC_VALUE_AND (age_bits [i * 2 + 1 ], ~unmarked );
42584233 }
42594234 }
42604235
0 commit comments