this repo has no description

Store heap object size in "header" (gc_obj.tag)

We know this at allocation time; no need to recompute in GC. This takes
no extra space since we're not using the upper bits of the tag word
anyway.

authored by bernsteinbear.com and committed by

Max Bernstein 48e89116 293ea5f2

+46 -61
+46 -61
runtime.c
··· 16 16 const int kPointerSize = sizeof(void*); 17 17 typedef intptr_t word; 18 18 typedef uintptr_t uword; 19 + typedef unsigned char byte; 19 20 20 21 // Garbage collector core by Andy Wingo <wingo@pobox.com>. 21 22 ··· 249 250 return heap_tag(addr); 250 251 } 251 252 253 + bool is_power_of_two(uword x) { return (x & (x - 1)) == 0; } 254 + 255 + bool is_aligned(uword value, uword alignment) { 256 + assert(is_power_of_two(alignment)); 257 + return (value & (alignment - 1)) == 0; 258 + } 259 + 260 + uword make_tag(uword tag, uword size_bytes) { 261 + assert(size_bytes <= 0xffffffff); 262 + return (size_bytes << kBitsPerByte) | tag; 263 + } 264 + 265 + byte obj_tag(struct gc_obj* obj) { return (obj->tag & 0xff); } 266 + 267 + bool obj_has_tag(struct gc_obj* obj, byte tag) { return obj_tag(obj) == tag; } 268 + 252 269 static ALWAYS_INLINE ALLOCATOR struct object* allocate(struct gc_heap* heap, 253 - size_t size) { 270 + uword tag, uword size) { 271 + assert(is_aligned(size, 1 << kPrimaryTagBits) && "need 3 bits for tagging"); 254 272 uintptr_t addr = heap->hp; 255 273 uintptr_t new_hp = align_size(addr + size); 256 274 if (UNLIKELY(heap->limit < new_hp)) { 257 275 return allocate_slow_path(heap, size); 258 276 } 259 277 heap->hp = new_hp; 278 + ((struct gc_obj*)addr)->tag = make_tag(tag, size); 260 279 return heap_tag(addr); 261 280 } 262 281 ··· 316 335 struct object* value; 317 336 }; 318 337 319 - size_t variable_size(size_t base, size_t count) { 320 - return base + count * kPointerSize; 321 - } 322 - 323 - size_t record_size(size_t count) { 324 - return sizeof(struct record) + count * sizeof(struct record_field); 325 - } 326 - 327 - size_t heap_string_size(size_t count) { 328 - return sizeof(struct heap_string) + count; 329 - } 330 - 331 - size_t heap_object_size(struct gc_obj* obj) { 332 - switch (obj->tag) { 333 - case TAG_LIST: 334 - return sizeof(struct list); 335 - case TAG_CLOSURE: 336 - return variable_size(sizeof(struct closure), 337 - ((struct closure*)obj)->size); 338 - case TAG_RECORD: 339 - return record_size(((struct record*)obj)->size); 340 - case TAG_STRING: 341 - return heap_string_size(((struct heap_string*)obj)->size); 342 - case TAG_VARIANT: 343 - return sizeof(struct variant); 344 - default: 345 - fprintf(stderr, "unknown tag: %lu\n", obj->tag); 346 - abort(); 347 - // Pacify the C compiler 348 - return (size_t)-1; 349 - } 350 - } 338 + size_t heap_object_size(struct gc_obj* obj) { return obj->tag >> kBitsPerByte; } 351 339 352 340 size_t trace_heap_object(struct gc_obj* obj, struct gc_heap* heap, 353 341 VisitFn visit) { 354 - switch (obj->tag) { 342 + switch (obj_tag(obj)) { 355 343 case TAG_LIST: 356 344 visit(&((struct list*)obj)->first, heap); 357 345 visit(&((struct list*)obj)->rest, heap); ··· 372 360 visit(&((struct variant*)obj)->value, heap); 373 361 break; 374 362 default: 375 - fprintf(stderr, "unknown tag: %lu\n", obj->tag); 363 + fprintf(stderr, "unknown tag: %u\n", obj_tag(obj)); 376 364 abort(); 377 365 } 378 366 return heap_object_size(obj); ··· 411 399 if (is_empty_list(obj)) { 412 400 return true; 413 401 } 414 - return is_heap_object(obj) && as_heap_object(obj)->tag == TAG_LIST; 402 + return is_heap_object(obj) && obj_has_tag(as_heap_object(obj), TAG_LIST); 415 403 } 416 404 417 405 struct list* as_list(struct object* obj) { ··· 430 418 } 431 419 432 420 struct object* mklist(struct gc_heap* heap) { 433 - struct object* result = allocate(heap, sizeof(struct list)); 434 - as_heap_object(result)->tag = TAG_LIST; 421 + struct object* result = allocate(heap, TAG_LIST, sizeof(struct list)); 435 422 as_list(result)->first = empty_list(); 436 423 as_list(result)->rest = empty_list(); 437 424 return result; 438 425 } 439 426 440 427 bool is_closure(struct object* obj) { 441 - return is_heap_object(obj) && as_heap_object(obj)->tag == TAG_CLOSURE; 428 + return is_heap_object(obj) && obj_has_tag(as_heap_object(obj), TAG_CLOSURE); 442 429 } 443 430 444 431 struct closure* as_closure(struct object* obj) { ··· 446 433 return (struct closure*)as_heap_object(obj); 447 434 } 448 435 449 - struct object* mkclosure(struct gc_heap* heap, ClosureFn fn, size_t size) { 450 - struct object* result = 451 - allocate(heap, variable_size(sizeof(struct closure), size)); 452 - as_heap_object(result)->tag = TAG_CLOSURE; 436 + struct object* mkclosure(struct gc_heap* heap, ClosureFn fn, 437 + size_t num_fields) { 438 + struct object* result = allocate( 439 + heap, TAG_CLOSURE, sizeof(struct closure) + num_fields * kPointerSize); 453 440 as_closure(result)->fn = fn; 454 - as_closure(result)->size = size; 441 + as_closure(result)->size = num_fields; 455 442 // Assumes the items will be filled in immediately after calling mkclosure so 456 443 // they are not initialized 457 444 return result; ··· 477 464 } 478 465 479 466 bool is_record(struct object* obj) { 480 - return is_heap_object(obj) && as_heap_object(obj)->tag == TAG_RECORD; 467 + return is_heap_object(obj) && obj_has_tag(as_heap_object(obj), TAG_RECORD); 481 468 } 482 469 483 470 struct record* as_record(struct object* obj) { ··· 485 472 return (struct record*)as_heap_object(obj); 486 473 } 487 474 488 - struct object* mkrecord(struct gc_heap* heap, size_t size) { 489 - // size is the number of fields, each of which has an index and a value 490 - // (object) 491 - struct object* result = allocate(heap, record_size(size)); 492 - as_heap_object(result)->tag = TAG_RECORD; 493 - as_record(result)->size = size; 475 + struct object* mkrecord(struct gc_heap* heap, size_t num_fields) { 476 + struct object* result = allocate( 477 + heap, TAG_RECORD, 478 + sizeof(struct record) + num_fields * sizeof(struct record_field)); 479 + as_record(result)->size = num_fields; 494 480 // Assumes the items will be filled in immediately after calling mkrecord so 495 481 // they are not initialized 496 482 return result; ··· 519 505 if (is_small_string(obj)) { 520 506 return true; 521 507 } 522 - return is_heap_object(obj) && as_heap_object(obj)->tag == TAG_STRING; 508 + return is_heap_object(obj) && obj_has_tag(as_heap_object(obj), TAG_STRING); 523 509 } 524 510 525 511 struct heap_string* as_heap_string(struct object* obj) { ··· 527 513 return (struct heap_string*)as_heap_object(obj); 528 514 } 529 515 530 - struct object* mkstring_uninit_private(struct gc_heap* heap, size_t size) { 531 - assert(size > kMaxSmallStringLength); // can't fill in small string later 532 - struct object* result = allocate(heap, heap_string_size(size)); 533 - as_heap_object(result)->tag = TAG_STRING; 534 - as_heap_string(result)->size = size; 516 + struct object* mkstring_uninit_private(struct gc_heap* heap, size_t count) { 517 + assert(count > kMaxSmallStringLength); // can't fill in small string later 518 + struct object* result = 519 + allocate(heap, TAG_STRING, sizeof(struct heap_string) + count); 520 + as_heap_string(result)->size = count; 535 521 return result; 536 522 } 537 523 ··· 562 548 if (is_immediate_variant(obj)) { 563 549 return true; 564 550 } 565 - return is_heap_object(obj) && as_heap_object(obj)->tag == TAG_VARIANT; 551 + return is_heap_object(obj) && obj_has_tag(as_heap_object(obj), TAG_VARIANT); 566 552 } 567 553 568 554 struct variant* as_variant(struct object* obj) { ··· 572 558 } 573 559 574 560 struct object* mkvariant(struct gc_heap* heap, size_t tag) { 575 - struct object* result = allocate(heap, sizeof(struct variant)); 576 - as_heap_object(result)->tag = TAG_VARIANT; 561 + struct object* result = allocate(heap, TAG_VARIANT, sizeof(struct variant)); 577 562 as_variant(result)->tag = tag; 578 563 return result; 579 564 } ··· 759 744 fputs("()", stdout); 760 745 } else { 761 746 assert(is_heap_object(obj)); 762 - fprintf(stderr, "unknown tag: %lu\n", as_heap_object(obj)->tag); 747 + fprintf(stderr, "unknown tag: %u\n", obj_tag(as_heap_object(obj))); 763 748 abort(); 764 749 } 765 750 return obj;