@@ -80,7 +80,7 @@ static ALWAYS_INLINE char small_string_at(struct object* obj, uword index) {
8080 // +1 for (length | tag) byte
8181 return ((uword )obj >> ((index + 1 ) * kBitsPerByte )) & 0xFF ;
8282}
83- struct gc_obj * as_heap_object (struct object * obj ) {
83+ static ALWAYS_INLINE struct gc_obj * as_heap_object (struct object * obj ) {
8484 assert (is_heap_object (obj ));
8585 assert (kHeapObjectTag == 1 );
8686 return (struct gc_obj * )((uword )obj - 1 );
@@ -128,7 +128,10 @@ static ALWAYS_INLINE uintptr_t align(uintptr_t val, uintptr_t alignment) {
128128 return (val + alignment - 1 ) & ~(alignment - 1 );
129129}
130130static ALWAYS_INLINE uintptr_t align_size (uintptr_t size ) {
131- return align (size , sizeof (uintptr_t ));
131+ return align (size , kObjectAlignment );
132+ }
133+ static ALWAYS_INLINE bool is_size_aligned (uword size ) {
134+ return size == align_size (size );
132135}
133136
134137#ifdef STATIC_HEAP
@@ -163,20 +166,23 @@ void init_heap(struct gc_heap* heap, struct space space) {
163166 heap -> from_space = heap -> limit = heap -> hp + space .size / 2 ;
164167}
165168
166- static ALWAYS_INLINE bool is_power_of_two (uword x ) { return (x & (x - 1 )) == 0 ; }
167-
168- static ALWAYS_INLINE bool is_aligned (uword value , uword alignment ) {
169- assert (is_power_of_two (alignment ));
170- return (value & (alignment - 1 )) == 0 ;
169+ static ALWAYS_INLINE uintptr_t heap_ptr (struct gc_heap * heap ) {
170+ #if defined(NDEBUG ) && defined(__GNUC__ )
171+ // Clang and GCC support this; TCC does not
172+ return (uintptr_t )__builtin_assume_aligned ((void * )heap -> hp , kObjectAlignment );
173+ #else
174+ assert (is_size_aligned (heap -> hp ) && "need 3 bits for tagging" );
175+ return heap -> hp ;
176+ #endif
171177}
172178
173179struct gc_obj * copy (struct gc_heap * heap , struct gc_obj * obj ) {
174180 size_t size = heap_object_size (obj );
175- struct gc_obj * new_obj = (struct gc_obj * )heap -> hp ;
181+ struct gc_obj * new_obj = (struct gc_obj * )heap_ptr ( heap ) ;
176182 memcpy (new_obj , obj , size );
177183 forward (obj , new_obj );
178- heap -> hp += align_size ( size ) ;
179- assert (is_aligned (heap -> hp , 1 << kPrimaryTagBits ) && "need 3 bits for tagging" );
184+ heap -> hp += size ;
185+ assert (is_size_aligned (heap -> hp ) && "need 3 bits for tagging" );
180186 return new_obj ;
181187}
182188
@@ -309,28 +315,38 @@ byte obj_tag(struct gc_obj* obj) { return (obj->tag & 0xff); }
309315
310316bool obj_has_tag (struct gc_obj * obj , byte tag ) { return obj_tag (obj ) == tag ; }
311317
312- static NEVER_INLINE void allocate_slow_path (struct gc_heap * heap , uword size ) {
318+ static NEVER_INLINE ALLOCATOR struct object * allocate_slow_path (struct gc_heap * heap , uword tag , uword size ) {
319+ // Outlining allocate_slow_path like this helps the compiler generate better
320+ // code in callers of allocate such as mklist. For some reason we have to
321+ // tail-duplicate allocate, too :(
313322#ifndef STATIC_HEAP
314323 heap_grow (heap );
315324#endif
316- // size is already aligned
325+ assert ( is_size_aligned ( size ) && "need 3 bits for tagging" );
317326 if (UNLIKELY (heap -> limit - heap -> hp < size )) {
318327 fprintf (stderr , "out of memory\n" );
319328 abort ();
320329 }
330+ // NOTE: Keep in sync with allocate
331+ uintptr_t addr = heap_ptr (heap );
332+ uintptr_t new_hp = addr + size ;
333+ assert (is_size_aligned (new_hp ) && "need 3 bits for tagging" );
334+ heap -> hp = new_hp ;
335+ ((struct gc_obj * )addr )-> tag = make_tag (tag , size );
336+ return heap_tag (addr );
321337}
322338
323339static ALWAYS_INLINE ALLOCATOR struct object * allocate (struct gc_heap * heap ,
324340 uword tag , uword size ) {
325- uintptr_t addr = heap -> hp ;
326- uintptr_t new_hp = align_size (addr + size );
327- assert (is_aligned (new_hp , 1 << kPrimaryTagBits ) && "need 3 bits for tagging" );
341+ assert (is_size_aligned (size ) && "need 3 bits for tagging" );
342+ // NOTE: Keep in sync with allocate_slow_path
343+ uintptr_t addr = heap_ptr (heap );
344+ uintptr_t new_hp = addr + size ;
345+ assert (is_size_aligned (new_hp ) && "need 3 bits for tagging" );
328346 if (UNLIKELY (heap -> limit < new_hp )) {
329- allocate_slow_path (heap , size );
330- addr = heap -> hp ;
331- new_hp = align_size (addr + size );
332- assert (is_aligned (new_hp , 1 << kPrimaryTagBits ) && "need 3 bits for tagging" );
347+ return allocate_slow_path (heap , tag , size );
333348 }
349+ // NOTE: Keep in sync with allocate_slow_path
334350 heap -> hp = new_hp ;
335351 ((struct gc_obj * )addr )-> tag = make_tag (tag , size );
336352 return heap_tag (addr );
@@ -352,11 +368,13 @@ enum {
352368#undef ENUM_TAG
353369};
354370
371+ #define HEAP_ALIGNED __attribute__((__aligned__(kObjectAlignment)))
372+
355373struct list {
356374 struct gc_obj HEAD ;
357375 struct object * first ;
358376 struct object * rest ;
359- };
377+ } HEAP_ALIGNED ;
360378
361379typedef struct object * (* ClosureFn )(struct object * , struct object * );
362380
@@ -367,7 +385,7 @@ struct closure {
367385 ClosureFn fn ;
368386 size_t size ;
369387 struct object * env [];
370- };
388+ }; // Not HEAP_ALIGNED; env is variable size
371389
372390struct record_field {
373391 size_t key ;
@@ -378,21 +396,25 @@ struct record {
378396 struct gc_obj HEAD ;
379397 size_t size ;
380398 struct record_field fields [];
381- };
399+ }; // Not HEAP_ALIGNED; fields is variable size
382400
383401struct heap_string {
384402 struct gc_obj HEAD ;
385403 size_t size ;
386404 char data [];
387- };
405+ }; // Not HEAP_ALIGNED; data is variable size
388406
389407struct variant {
390408 struct gc_obj HEAD ;
391409 size_t tag ;
392410 struct object * value ;
393- };
411+ } HEAP_ALIGNED ;
394412
395- size_t heap_object_size (struct gc_obj * obj ) { return obj -> tag >> kBitsPerByte ; }
413+ size_t heap_object_size (struct gc_obj * obj ) {
414+ size_t result = obj -> tag >> kBitsPerByte ;
415+ assert (is_size_aligned (result ));
416+ return result ;
417+ }
396418
397419size_t trace_heap_object (struct gc_obj * obj , struct gc_heap * heap ,
398420 VisitFn visit ) {
@@ -492,8 +514,8 @@ struct closure* as_closure(struct object* obj) {
492514
493515struct object * mkclosure (struct gc_heap * heap , ClosureFn fn ,
494516 size_t num_fields ) {
495- struct object * result = allocate (
496- heap , TAG_CLOSURE , sizeof ( struct closure ) + num_fields * kPointerSize );
517+ uword size = align_size ( sizeof ( struct closure ) + num_fields * kPointerSize );
518+ struct object * result = allocate ( heap , TAG_CLOSURE , size );
497519 as_closure (result )-> fn = fn ;
498520 as_closure (result )-> size = num_fields ;
499521 // Assumes the items will be filled in immediately after calling mkclosure so
@@ -530,9 +552,8 @@ struct record* as_record(struct object* obj) {
530552}
531553
532554struct object * mkrecord (struct gc_heap * heap , size_t num_fields ) {
533- struct object * result = allocate (
534- heap , TAG_RECORD ,
535- sizeof (struct record ) + num_fields * sizeof (struct record_field ));
555+ uword size = align_size (sizeof (struct record ) + num_fields * sizeof (struct record_field ));
556+ struct object * result = allocate (heap , TAG_RECORD , size );
536557 as_record (result )-> size = num_fields ;
537558 // Assumes the items will be filled in immediately after calling mkrecord so
538559 // they are not initialized
@@ -576,8 +597,8 @@ struct heap_string* as_heap_string(struct object* obj) {
576597
577598struct object * mkstring_uninit_private (struct gc_heap * heap , size_t count ) {
578599 assert (count > kMaxSmallStringLength ); // can't fill in small string later
579- struct object * result =
580- allocate (heap , TAG_STRING , sizeof ( struct heap_string ) + count );
600+ uword size = align_size ( sizeof ( struct heap_string ) + count );
601+ struct object * result = allocate (heap , TAG_STRING , size );
581602 as_heap_string (result )-> size = count ;
582603 return result ;
583604}
0 commit comments