diff options
| author | Mattias EngdegÄrd | 2022-04-02 16:02:09 +0200 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2022-04-04 19:15:42 +0200 |
| commit | 7a8798de95a57c8ff85f070075e0a0176b458578 (patch) | |
| tree | 0e4d9f09daeff6ae50d3a3c6b2b9e8c2cc0646fd /src/alloc.c | |
| parent | 8103b060d89ac63a12c439087bd46c30da72cd97 (diff) | |
| download | emacs-7a8798de95a57c8ff85f070075e0a0176b458578.tar.gz emacs-7a8798de95a57c8ff85f070075e0a0176b458578.zip | |
Reduce GC mark-phase recursion by using explicit stack (bug#54698)
An explict stack of objects to be traversed for marking replaces
recursion for most common object types: conses, vectors, records, hash
tables, symbols, functions etc. Recursion is still used for other
types but those are less common and thus not as likely to cause a
problem.
The stack grows dynamically as required which eliminates almost all C
stack overflow crashes in the GC. There is also a nontrivial GC
performance improvement.
* src/alloc.c (GC_REMEMBER_LAST_MARKED, GC_CDR_COUNT): New.
(mark_char_table, struct mark_entry):
Remove (subsumed into process_mark_stack).
(struct mark_entry, struct mark_stack, mark_stk)
(mark_stack_empty_p, mark_stack_pop, grow_mark_stack)
(mark_stack_push_value, mark_stack_push_values)
(process_mark_stack): New.
(mark_object, mark_objects):
Just push the object(s) and let process_mark_stack do the work.
Diffstat (limited to 'src/alloc.c')
| -rw-r--r-- | src/alloc.c | 618 |
1 files changed, 354 insertions, 264 deletions
diff --git a/src/alloc.c b/src/alloc.c index b06dd943ba5..71f2c199b22 100644 --- a/src/alloc.c +++ b/src/alloc.c | |||
| @@ -6085,6 +6085,8 @@ maybe_garbage_collect (void) | |||
| 6085 | garbage_collect (); | 6085 | garbage_collect (); |
| 6086 | } | 6086 | } |
| 6087 | 6087 | ||
| 6088 | static inline bool mark_stack_empty_p (void); | ||
| 6089 | |||
| 6088 | /* Subroutine of Fgarbage_collect that does most of the work. */ | 6090 | /* Subroutine of Fgarbage_collect that does most of the work. */ |
| 6089 | void | 6091 | void |
| 6090 | garbage_collect (void) | 6092 | garbage_collect (void) |
| @@ -6100,6 +6102,8 @@ garbage_collect (void) | |||
| 6100 | if (garbage_collection_inhibited) | 6102 | if (garbage_collection_inhibited) |
| 6101 | return; | 6103 | return; |
| 6102 | 6104 | ||
| 6105 | eassert(mark_stack_empty_p ()); | ||
| 6106 | |||
| 6103 | /* Record this function, so it appears on the profiler's backtraces. */ | 6107 | /* Record this function, so it appears on the profiler's backtraces. */ |
| 6104 | record_in_backtrace (QAutomatic_GC, 0, 0); | 6108 | record_in_backtrace (QAutomatic_GC, 0, 0); |
| 6105 | 6109 | ||
| @@ -6222,6 +6226,8 @@ garbage_collect (void) | |||
| 6222 | mark_and_sweep_weak_table_contents (); | 6226 | mark_and_sweep_weak_table_contents (); |
| 6223 | eassert (weak_hash_tables == NULL); | 6227 | eassert (weak_hash_tables == NULL); |
| 6224 | 6228 | ||
| 6229 | eassert (mark_stack_empty_p ()); | ||
| 6230 | |||
| 6225 | gc_sweep (); | 6231 | gc_sweep (); |
| 6226 | 6232 | ||
| 6227 | unmark_main_thread (); | 6233 | unmark_main_thread (); |
| @@ -6395,15 +6401,25 @@ mark_glyph_matrix (struct glyph_matrix *matrix) | |||
| 6395 | } | 6401 | } |
| 6396 | } | 6402 | } |
| 6397 | 6403 | ||
| 6404 | /* Whether to remember a few of the last marked values for debugging. */ | ||
| 6405 | #define GC_REMEMBER_LAST_MARKED 0 | ||
| 6406 | |||
| 6407 | #if GC_REMEMBER_LAST_MARKED | ||
| 6398 | enum { LAST_MARKED_SIZE = 1 << 9 }; /* Must be a power of 2. */ | 6408 | enum { LAST_MARKED_SIZE = 1 << 9 }; /* Must be a power of 2. */ |
| 6399 | Lisp_Object last_marked[LAST_MARKED_SIZE] EXTERNALLY_VISIBLE; | 6409 | Lisp_Object last_marked[LAST_MARKED_SIZE] EXTERNALLY_VISIBLE; |
| 6400 | static int last_marked_index; | 6410 | static int last_marked_index; |
| 6411 | #endif | ||
| 6401 | 6412 | ||
| 6413 | /* Whether to enable the mark_object_loop_halt debugging feature. */ | ||
| 6414 | #define GC_CDR_COUNT 0 | ||
| 6415 | |||
| 6416 | #if GC_CDR_COUNT | ||
| 6402 | /* For debugging--call abort when we cdr down this many | 6417 | /* For debugging--call abort when we cdr down this many |
| 6403 | links of a list, in mark_object. In debugging, | 6418 | links of a list, in mark_object. In debugging, |
| 6404 | the call to abort will hit a breakpoint. | 6419 | the call to abort will hit a breakpoint. |
| 6405 | Normally this is zero and the check never goes off. */ | 6420 | Normally this is zero and the check never goes off. */ |
| 6406 | ptrdiff_t mark_object_loop_halt EXTERNALLY_VISIBLE; | 6421 | ptrdiff_t mark_object_loop_halt EXTERNALLY_VISIBLE; |
| 6422 | #endif | ||
| 6407 | 6423 | ||
| 6408 | static void | 6424 | static void |
| 6409 | mark_vectorlike (union vectorlike_header *header) | 6425 | mark_vectorlike (union vectorlike_header *header) |
| @@ -6457,19 +6473,6 @@ mark_char_table (struct Lisp_Vector *ptr, enum pvec_type pvectype) | |||
| 6457 | } | 6473 | } |
| 6458 | } | 6474 | } |
| 6459 | 6475 | ||
| 6460 | NO_INLINE /* To reduce stack depth in mark_object. */ | ||
| 6461 | static Lisp_Object | ||
| 6462 | mark_compiled (struct Lisp_Vector *ptr) | ||
| 6463 | { | ||
| 6464 | int i, size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK; | ||
| 6465 | |||
| 6466 | set_vector_marked (ptr); | ||
| 6467 | for (i = 0; i < size; i++) | ||
| 6468 | if (i != COMPILED_CONSTANTS) | ||
| 6469 | mark_object (ptr->contents[i]); | ||
| 6470 | return size > COMPILED_CONSTANTS ? ptr->contents[COMPILED_CONSTANTS] : Qnil; | ||
| 6471 | } | ||
| 6472 | |||
| 6473 | /* Mark the chain of overlays starting at PTR. */ | 6476 | /* Mark the chain of overlays starting at PTR. */ |
| 6474 | 6477 | ||
| 6475 | static void | 6478 | static void |
| @@ -6622,110 +6625,161 @@ mark_window (struct Lisp_Vector *ptr) | |||
| 6622 | (w, mark_discard_killed_buffers (w->next_buffers)); | 6625 | (w, mark_discard_killed_buffers (w->next_buffers)); |
| 6623 | } | 6626 | } |
| 6624 | 6627 | ||
| 6625 | static void | 6628 | /* Entry of the mark stack. */ |
| 6626 | mark_hash_table (struct Lisp_Vector *ptr) | 6629 | struct mark_entry |
| 6627 | { | 6630 | { |
| 6628 | struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *) ptr; | 6631 | ptrdiff_t n; /* number of values, or 0 if a single value */ |
| 6629 | 6632 | union { | |
| 6630 | mark_vectorlike (&h->header); | 6633 | Lisp_Object value; /* when n = 0 */ |
| 6631 | mark_object (h->test.name); | 6634 | Lisp_Object *values; /* when n > 0 */ |
| 6632 | mark_object (h->test.user_hash_function); | 6635 | } u; |
| 6633 | mark_object (h->test.user_cmp_function); | 6636 | }; |
| 6634 | /* If hash table is not weak, mark all keys and values. For weak | 6637 | |
| 6635 | tables, mark only the vector and not its contents --- that's what | 6638 | /* This stack is used during marking for traversing data structures without |
| 6636 | makes it weak. */ | 6639 | using C recursion. */ |
| 6637 | if (NILP (h->weak)) | 6640 | struct mark_stack |
| 6638 | mark_object (h->key_and_value); | 6641 | { |
| 6639 | else | 6642 | struct mark_entry *stack; /* base of stack */ |
| 6643 | ptrdiff_t size; /* allocated size in entries */ | ||
| 6644 | ptrdiff_t sp; /* current number of entries */ | ||
| 6645 | }; | ||
| 6646 | |||
| 6647 | static struct mark_stack mark_stk = {NULL, 0, 0}; | ||
| 6648 | |||
| 6649 | static inline bool | ||
| 6650 | mark_stack_empty_p (void) | ||
| 6651 | { | ||
| 6652 | return mark_stk.sp <= 0; | ||
| 6653 | } | ||
| 6654 | |||
| 6655 | /* Pop and return a value from the mark stack (which must be nonempty). */ | ||
| 6656 | static inline Lisp_Object | ||
| 6657 | mark_stack_pop (void) | ||
| 6658 | { | ||
| 6659 | eassume (!mark_stack_empty_p ()); | ||
| 6660 | struct mark_entry *e = &mark_stk.stack[mark_stk.sp - 1]; | ||
| 6661 | if (e->n == 0) /* single value */ | ||
| 6640 | { | 6662 | { |
| 6641 | eassert (h->next_weak == NULL); | 6663 | --mark_stk.sp; |
| 6642 | h->next_weak = weak_hash_tables; | 6664 | return e->u.value; |
| 6643 | weak_hash_tables = h; | ||
| 6644 | set_vector_marked (XVECTOR (h->key_and_value)); | ||
| 6645 | } | 6665 | } |
| 6666 | /* Array of values: pop them left to right, which seems to be slightly | ||
| 6667 | faster than right to left. */ | ||
| 6668 | e->n--; | ||
| 6669 | if (e->n == 0) | ||
| 6670 | --mark_stk.sp; /* last value consumed */ | ||
| 6671 | return (++e->u.values)[-1]; | ||
| 6646 | } | 6672 | } |
| 6647 | 6673 | ||
| 6648 | void | 6674 | NO_INLINE static void |
| 6649 | mark_objects (Lisp_Object *obj, ptrdiff_t n) | 6675 | grow_mark_stack (void) |
| 6650 | { | 6676 | { |
| 6651 | for (ptrdiff_t i = 0; i < n; i++) | 6677 | struct mark_stack *ms = &mark_stk; |
| 6652 | mark_object (obj[i]); | 6678 | eassert (ms->sp == ms->size); |
| 6679 | ptrdiff_t min_incr = ms->sp == 0 ? 8192 : 1; | ||
| 6680 | ptrdiff_t oldsize = ms->size; | ||
| 6681 | ms->stack = xpalloc (ms->stack, &ms->size, min_incr, -1, sizeof *ms->stack); | ||
| 6682 | eassert (ms->sp < ms->size); | ||
| 6653 | } | 6683 | } |
| 6654 | 6684 | ||
| 6655 | /* Determine type of generic Lisp_Object and mark it accordingly. | 6685 | /* Push VALUE onto the mark stack. */ |
| 6686 | static inline void | ||
| 6687 | mark_stack_push_value (Lisp_Object value) | ||
| 6688 | { | ||
| 6689 | if (mark_stk.sp >= mark_stk.size) | ||
| 6690 | grow_mark_stack (); | ||
| 6691 | mark_stk.stack[mark_stk.sp++] = (struct mark_entry){.n = 0, .u.value = value}; | ||
| 6692 | } | ||
| 6656 | 6693 | ||
| 6657 | This function implements a straightforward depth-first marking | 6694 | /* Push the N values at VALUES onto the mark stack. */ |
| 6658 | algorithm and so the recursion depth may be very high (a few | 6695 | static inline void |
| 6659 | tens of thousands is not uncommon). To minimize stack usage, | 6696 | mark_stack_push_values (Lisp_Object *values, ptrdiff_t n) |
| 6660 | a few cold paths are moved out to NO_INLINE functions above. | 6697 | { |
| 6661 | In general, inlining them doesn't help you to gain more speed. */ | 6698 | eassume (n >= 0); |
| 6699 | if (n == 0) | ||
| 6700 | return; | ||
| 6701 | if (mark_stk.sp >= mark_stk.size) | ||
| 6702 | grow_mark_stack (); | ||
| 6703 | mark_stk.stack[mark_stk.sp++] = (struct mark_entry){.n = n, | ||
| 6704 | .u.values = values}; | ||
| 6705 | } | ||
| 6662 | 6706 | ||
| 6663 | void | 6707 | /* Traverse and mark objects on the mark stack above BASE_SP. |
| 6664 | mark_object (Lisp_Object arg) | 6708 | |
| 6709 | Traversal is depth-first using the mark stack for most common | ||
| 6710 | object types. Recursion is used for other types, in the hope that | ||
| 6711 | they are rare enough that C stack usage is kept low. */ | ||
| 6712 | static void | ||
| 6713 | process_mark_stack (ptrdiff_t base_sp) | ||
| 6665 | { | 6714 | { |
| 6666 | register Lisp_Object obj; | ||
| 6667 | void *po; | ||
| 6668 | #if GC_CHECK_MARKED_OBJECTS | 6715 | #if GC_CHECK_MARKED_OBJECTS |
| 6669 | struct mem_node *m = NULL; | 6716 | struct mem_node *m = NULL; |
| 6670 | #endif | 6717 | #endif |
| 6718 | #if GC_CDR_COUNT | ||
| 6671 | ptrdiff_t cdr_count = 0; | 6719 | ptrdiff_t cdr_count = 0; |
| 6720 | #endif | ||
| 6672 | 6721 | ||
| 6673 | obj = arg; | 6722 | eassume (mark_stk.sp >= base_sp && base_sp >= 0); |
| 6674 | loop: | ||
| 6675 | 6723 | ||
| 6676 | po = XPNTR (obj); | 6724 | while (mark_stk.sp > base_sp) |
| 6677 | if (PURE_P (po)) | 6725 | { |
| 6678 | return; | 6726 | Lisp_Object obj = mark_stack_pop (); |
| 6727 | mark_obj: ; | ||
| 6728 | void *po = XPNTR (obj); | ||
| 6729 | if (PURE_P (po)) | ||
| 6730 | continue; | ||
| 6679 | 6731 | ||
| 6680 | last_marked[last_marked_index++] = obj; | 6732 | #if GC_REMEMBER_LAST_MARKED |
| 6681 | last_marked_index &= LAST_MARKED_SIZE - 1; | 6733 | last_marked[last_marked_index++] = obj; |
| 6734 | last_marked_index &= LAST_MARKED_SIZE - 1; | ||
| 6735 | #endif | ||
| 6682 | 6736 | ||
| 6683 | /* Perform some sanity checks on the objects marked here. Abort if | 6737 | /* Perform some sanity checks on the objects marked here. Abort if |
| 6684 | we encounter an object we know is bogus. This increases GC time | 6738 | we encounter an object we know is bogus. This increases GC time |
| 6685 | by ~80%. */ | 6739 | by ~80%. */ |
| 6686 | #if GC_CHECK_MARKED_OBJECTS | 6740 | #if GC_CHECK_MARKED_OBJECTS |
| 6687 | 6741 | ||
| 6688 | /* Check that the object pointed to by PO is known to be a Lisp | 6742 | /* Check that the object pointed to by PO is known to be a Lisp |
| 6689 | structure allocated from the heap. */ | 6743 | structure allocated from the heap. */ |
| 6690 | #define CHECK_ALLOCATED() \ | 6744 | #define CHECK_ALLOCATED() \ |
| 6691 | do { \ | 6745 | do { \ |
| 6692 | if (pdumper_object_p (po)) \ | 6746 | if (pdumper_object_p (po)) \ |
| 6693 | { \ | 6747 | { \ |
| 6694 | if (!pdumper_object_p_precise (po)) \ | 6748 | if (!pdumper_object_p_precise (po)) \ |
| 6695 | emacs_abort (); \ | 6749 | emacs_abort (); \ |
| 6696 | break; \ | 6750 | break; \ |
| 6697 | } \ | 6751 | } \ |
| 6698 | m = mem_find (po); \ | 6752 | m = mem_find (po); \ |
| 6699 | if (m == MEM_NIL) \ | 6753 | if (m == MEM_NIL) \ |
| 6700 | emacs_abort (); \ | 6754 | emacs_abort (); \ |
| 6701 | } while (0) | 6755 | } while (0) |
| 6702 | 6756 | ||
| 6703 | /* Check that the object pointed to by PO is live, using predicate | 6757 | /* Check that the object pointed to by PO is live, using predicate |
| 6704 | function LIVEP. */ | 6758 | function LIVEP. */ |
| 6705 | #define CHECK_LIVE(LIVEP, MEM_TYPE) \ | 6759 | #define CHECK_LIVE(LIVEP, MEM_TYPE) \ |
| 6706 | do { \ | 6760 | do { \ |
| 6707 | if (pdumper_object_p (po)) \ | 6761 | if (pdumper_object_p (po)) \ |
| 6708 | break; \ | 6762 | break; \ |
| 6709 | if (! (m->type == MEM_TYPE && LIVEP (m, po))) \ | 6763 | if (! (m->type == MEM_TYPE && LIVEP (m, po))) \ |
| 6710 | emacs_abort (); \ | 6764 | emacs_abort (); \ |
| 6711 | } while (0) | 6765 | } while (0) |
| 6712 | 6766 | ||
| 6713 | /* Check both of the above conditions, for non-symbols. */ | 6767 | /* Check both of the above conditions, for non-symbols. */ |
| 6714 | #define CHECK_ALLOCATED_AND_LIVE(LIVEP, MEM_TYPE) \ | 6768 | #define CHECK_ALLOCATED_AND_LIVE(LIVEP, MEM_TYPE) \ |
| 6715 | do { \ | 6769 | do { \ |
| 6716 | CHECK_ALLOCATED (); \ | 6770 | CHECK_ALLOCATED (); \ |
| 6717 | CHECK_LIVE (LIVEP, MEM_TYPE); \ | 6771 | CHECK_LIVE (LIVEP, MEM_TYPE); \ |
| 6718 | } while (false) | 6772 | } while (false) |
| 6719 | 6773 | ||
| 6720 | /* Check both of the above conditions, for symbols. */ | 6774 | /* Check both of the above conditions, for symbols. */ |
| 6721 | #define CHECK_ALLOCATED_AND_LIVE_SYMBOL() \ | 6775 | #define CHECK_ALLOCATED_AND_LIVE_SYMBOL() \ |
| 6722 | do { \ | 6776 | do { \ |
| 6723 | if (!c_symbol_p (ptr)) \ | 6777 | if (!c_symbol_p (ptr)) \ |
| 6724 | { \ | 6778 | { \ |
| 6725 | CHECK_ALLOCATED (); \ | 6779 | CHECK_ALLOCATED (); \ |
| 6726 | CHECK_LIVE (live_symbol_p, MEM_TYPE_SYMBOL); \ | 6780 | CHECK_LIVE (live_symbol_p, MEM_TYPE_SYMBOL); \ |
| 6727 | } \ | 6781 | } \ |
| 6728 | } while (false) | 6782 | } while (false) |
| 6729 | 6783 | ||
| 6730 | #else /* not GC_CHECK_MARKED_OBJECTS */ | 6784 | #else /* not GC_CHECK_MARKED_OBJECTS */ |
| 6731 | 6785 | ||
| @@ -6734,200 +6788,220 @@ mark_object (Lisp_Object arg) | |||
| 6734 | 6788 | ||
| 6735 | #endif /* not GC_CHECK_MARKED_OBJECTS */ | 6789 | #endif /* not GC_CHECK_MARKED_OBJECTS */ |
| 6736 | 6790 | ||
| 6737 | switch (XTYPE (obj)) | 6791 | switch (XTYPE (obj)) |
| 6738 | { | 6792 | { |
| 6739 | case Lisp_String: | 6793 | case Lisp_String: |
| 6740 | { | 6794 | { |
| 6741 | register struct Lisp_String *ptr = XSTRING (obj); | 6795 | register struct Lisp_String *ptr = XSTRING (obj); |
| 6742 | if (string_marked_p (ptr)) | 6796 | if (string_marked_p (ptr)) |
| 6743 | break; | 6797 | break; |
| 6744 | CHECK_ALLOCATED_AND_LIVE (live_string_p, MEM_TYPE_STRING); | 6798 | CHECK_ALLOCATED_AND_LIVE (live_string_p, MEM_TYPE_STRING); |
| 6745 | set_string_marked (ptr); | 6799 | set_string_marked (ptr); |
| 6746 | mark_interval_tree (ptr->u.s.intervals); | 6800 | mark_interval_tree (ptr->u.s.intervals); |
| 6747 | #ifdef GC_CHECK_STRING_BYTES | 6801 | #ifdef GC_CHECK_STRING_BYTES |
| 6748 | /* Check that the string size recorded in the string is the | 6802 | /* Check that the string size recorded in the string is the |
| 6749 | same as the one recorded in the sdata structure. */ | 6803 | same as the one recorded in the sdata structure. */ |
| 6750 | string_bytes (ptr); | 6804 | string_bytes (ptr); |
| 6751 | #endif /* GC_CHECK_STRING_BYTES */ | 6805 | #endif /* GC_CHECK_STRING_BYTES */ |
| 6752 | } | 6806 | } |
| 6753 | break; | 6807 | break; |
| 6754 | 6808 | ||
| 6755 | case Lisp_Vectorlike: | 6809 | case Lisp_Vectorlike: |
| 6756 | { | 6810 | { |
| 6757 | register struct Lisp_Vector *ptr = XVECTOR (obj); | 6811 | register struct Lisp_Vector *ptr = XVECTOR (obj); |
| 6758 | 6812 | ||
| 6759 | if (vector_marked_p (ptr)) | 6813 | if (vector_marked_p (ptr)) |
| 6760 | break; | 6814 | break; |
| 6761 | 6815 | ||
| 6762 | enum pvec_type pvectype | 6816 | enum pvec_type pvectype |
| 6763 | = PSEUDOVECTOR_TYPE (ptr); | 6817 | = PSEUDOVECTOR_TYPE (ptr); |
| 6764 | 6818 | ||
| 6765 | #ifdef GC_CHECK_MARKED_OBJECTS | 6819 | #ifdef GC_CHECK_MARKED_OBJECTS |
| 6766 | if (!pdumper_object_p (po) && !SUBRP (obj) && !main_thread_p (po)) | 6820 | if (!pdumper_object_p (po) && !SUBRP (obj) && !main_thread_p (po)) |
| 6767 | { | 6821 | { |
| 6768 | m = mem_find (po); | 6822 | m = mem_find (po); |
| 6769 | if (m == MEM_NIL) | 6823 | if (m == MEM_NIL) |
| 6770 | emacs_abort (); | 6824 | emacs_abort (); |
| 6771 | if (m->type == MEM_TYPE_VECTORLIKE) | 6825 | if (m->type == MEM_TYPE_VECTORLIKE) |
| 6772 | CHECK_LIVE (live_large_vector_p, MEM_TYPE_VECTORLIKE); | 6826 | CHECK_LIVE (live_large_vector_p, MEM_TYPE_VECTORLIKE); |
| 6773 | else | 6827 | else |
| 6774 | CHECK_LIVE (live_small_vector_p, MEM_TYPE_VECTOR_BLOCK); | 6828 | CHECK_LIVE (live_small_vector_p, MEM_TYPE_VECTOR_BLOCK); |
| 6775 | } | 6829 | } |
| 6776 | #endif | 6830 | #endif |
| 6777 | 6831 | ||
| 6778 | switch (pvectype) | 6832 | switch (pvectype) |
| 6779 | { | ||
| 6780 | case PVEC_BUFFER: | ||
| 6781 | mark_buffer ((struct buffer *) ptr); | ||
| 6782 | break; | ||
| 6783 | |||
| 6784 | case PVEC_COMPILED: | ||
| 6785 | /* Although we could treat this just like a vector, mark_compiled | ||
| 6786 | returns the COMPILED_CONSTANTS element, which is marked at the | ||
| 6787 | next iteration of goto-loop here. This is done to avoid a few | ||
| 6788 | recursive calls to mark_object. */ | ||
| 6789 | obj = mark_compiled (ptr); | ||
| 6790 | if (!NILP (obj)) | ||
| 6791 | goto loop; | ||
| 6792 | break; | ||
| 6793 | |||
| 6794 | case PVEC_FRAME: | ||
| 6795 | mark_frame (ptr); | ||
| 6796 | break; | ||
| 6797 | |||
| 6798 | case PVEC_WINDOW: | ||
| 6799 | mark_window (ptr); | ||
| 6800 | break; | ||
| 6801 | |||
| 6802 | case PVEC_HASH_TABLE: | ||
| 6803 | mark_hash_table (ptr); | ||
| 6804 | break; | ||
| 6805 | |||
| 6806 | case PVEC_CHAR_TABLE: | ||
| 6807 | case PVEC_SUB_CHAR_TABLE: | ||
| 6808 | mark_char_table (ptr, (enum pvec_type) pvectype); | ||
| 6809 | break; | ||
| 6810 | |||
| 6811 | case PVEC_BOOL_VECTOR: | ||
| 6812 | /* bool vectors in a dump are permanently "marked", since | ||
| 6813 | they're in the old section and don't have mark bits. | ||
| 6814 | If we're looking at a dumped bool vector, we should | ||
| 6815 | have aborted above when we called vector_marked_p, so | ||
| 6816 | we should never get here. */ | ||
| 6817 | eassert (!pdumper_object_p (ptr)); | ||
| 6818 | set_vector_marked (ptr); | ||
| 6819 | break; | ||
| 6820 | |||
| 6821 | case PVEC_OVERLAY: | ||
| 6822 | mark_overlay (XOVERLAY (obj)); | ||
| 6823 | break; | ||
| 6824 | |||
| 6825 | case PVEC_SUBR: | ||
| 6826 | #ifdef HAVE_NATIVE_COMP | ||
| 6827 | if (SUBR_NATIVE_COMPILEDP (obj)) | ||
| 6828 | { | 6833 | { |
| 6834 | case PVEC_BUFFER: | ||
| 6835 | mark_buffer ((struct buffer *) ptr); | ||
| 6836 | break; | ||
| 6837 | |||
| 6838 | case PVEC_FRAME: | ||
| 6839 | mark_frame (ptr); | ||
| 6840 | break; | ||
| 6841 | |||
| 6842 | case PVEC_WINDOW: | ||
| 6843 | mark_window (ptr); | ||
| 6844 | break; | ||
| 6845 | |||
| 6846 | case PVEC_HASH_TABLE: | ||
| 6847 | { | ||
| 6848 | struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *)ptr; | ||
| 6849 | ptrdiff_t size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK; | ||
| 6850 | set_vector_marked (ptr); | ||
| 6851 | mark_stack_push_values (ptr->contents, size); | ||
| 6852 | mark_stack_push_value (h->test.name); | ||
| 6853 | mark_stack_push_value (h->test.user_hash_function); | ||
| 6854 | mark_stack_push_value (h->test.user_cmp_function); | ||
| 6855 | if (NILP (h->weak)) | ||
| 6856 | mark_stack_push_value (h->key_and_value); | ||
| 6857 | else | ||
| 6858 | { | ||
| 6859 | /* For weak tables, mark only the vector and not its | ||
| 6860 | contents --- that's what makes it weak. */ | ||
| 6861 | eassert (h->next_weak == NULL); | ||
| 6862 | h->next_weak = weak_hash_tables; | ||
| 6863 | weak_hash_tables = h; | ||
| 6864 | set_vector_marked (XVECTOR (h->key_and_value)); | ||
| 6865 | } | ||
| 6866 | break; | ||
| 6867 | } | ||
| 6868 | |||
| 6869 | case PVEC_CHAR_TABLE: | ||
| 6870 | case PVEC_SUB_CHAR_TABLE: | ||
| 6871 | mark_char_table (ptr, (enum pvec_type) pvectype); | ||
| 6872 | break; | ||
| 6873 | |||
| 6874 | case PVEC_BOOL_VECTOR: | ||
| 6875 | /* bool vectors in a dump are permanently "marked", since | ||
| 6876 | they're in the old section and don't have mark bits. | ||
| 6877 | If we're looking at a dumped bool vector, we should | ||
| 6878 | have aborted above when we called vector_marked_p, so | ||
| 6879 | we should never get here. */ | ||
| 6880 | eassert (!pdumper_object_p (ptr)); | ||
| 6829 | set_vector_marked (ptr); | 6881 | set_vector_marked (ptr); |
| 6830 | struct Lisp_Subr *subr = XSUBR (obj); | 6882 | break; |
| 6831 | mark_object (subr->native_intspec); | 6883 | |
| 6832 | mark_object (subr->command_modes); | 6884 | case PVEC_OVERLAY: |
| 6833 | mark_object (subr->native_comp_u); | 6885 | mark_overlay (XOVERLAY (obj)); |
| 6834 | mark_object (subr->lambda_list); | 6886 | break; |
| 6835 | mark_object (subr->type); | 6887 | |
| 6836 | } | 6888 | case PVEC_SUBR: |
| 6889 | #ifdef HAVE_NATIVE_COMP | ||
| 6890 | if (SUBR_NATIVE_COMPILEDP (obj)) | ||
| 6891 | { | ||
| 6892 | set_vector_marked (ptr); | ||
| 6893 | struct Lisp_Subr *subr = XSUBR (obj); | ||
| 6894 | mark_stack_push_value (subr->native_intspec); | ||
| 6895 | mark_stack_push_value (subr->command_modes); | ||
| 6896 | mark_stack_push_value (subr->native_comp_u); | ||
| 6897 | mark_stack_push_value (subr->lambda_list); | ||
| 6898 | mark_stack_push_value (subr->type); | ||
| 6899 | } | ||
| 6837 | #endif | 6900 | #endif |
| 6838 | break; | 6901 | break; |
| 6839 | 6902 | ||
| 6840 | case PVEC_FREE: | 6903 | case PVEC_FREE: |
| 6841 | emacs_abort (); | 6904 | emacs_abort (); |
| 6842 | 6905 | ||
| 6843 | default: | 6906 | default: |
| 6844 | /* A regular vector, or a pseudovector needing no special | 6907 | { |
| 6845 | treatment. */ | 6908 | /* A regular vector or pseudovector needing no special |
| 6846 | mark_vectorlike (&ptr->header); | 6909 | treatment. */ |
| 6910 | ptrdiff_t size = ptr->header.size; | ||
| 6911 | if (size & PSEUDOVECTOR_FLAG) | ||
| 6912 | size &= PSEUDOVECTOR_SIZE_MASK; | ||
| 6913 | set_vector_marked (ptr); | ||
| 6914 | mark_stack_push_values (ptr->contents, size); | ||
| 6915 | } | ||
| 6916 | break; | ||
| 6917 | } | ||
| 6847 | } | 6918 | } |
| 6848 | } | 6919 | break; |
| 6849 | break; | ||
| 6850 | 6920 | ||
| 6851 | case Lisp_Symbol: | 6921 | case Lisp_Symbol: |
| 6852 | { | ||
| 6853 | struct Lisp_Symbol *ptr = XBARE_SYMBOL (obj); | ||
| 6854 | nextsym: | ||
| 6855 | if (symbol_marked_p (ptr)) | ||
| 6856 | break; | ||
| 6857 | CHECK_ALLOCATED_AND_LIVE_SYMBOL (); | ||
| 6858 | set_symbol_marked (ptr); | ||
| 6859 | /* Attempt to catch bogus objects. */ | ||
| 6860 | eassert (valid_lisp_object_p (ptr->u.s.function)); | ||
| 6861 | mark_object (ptr->u.s.function); | ||
| 6862 | mark_object (ptr->u.s.plist); | ||
| 6863 | switch (ptr->u.s.redirect) | ||
| 6864 | { | 6922 | { |
| 6865 | case SYMBOL_PLAINVAL: mark_object (SYMBOL_VAL (ptr)); break; | 6923 | struct Lisp_Symbol *ptr = XBARE_SYMBOL (obj); |
| 6866 | case SYMBOL_VARALIAS: | 6924 | nextsym: |
| 6867 | { | 6925 | if (symbol_marked_p (ptr)) |
| 6868 | Lisp_Object tem; | ||
| 6869 | XSETSYMBOL (tem, SYMBOL_ALIAS (ptr)); | ||
| 6870 | mark_object (tem); | ||
| 6871 | break; | 6926 | break; |
| 6872 | } | 6927 | CHECK_ALLOCATED_AND_LIVE_SYMBOL (); |
| 6873 | case SYMBOL_LOCALIZED: | 6928 | set_symbol_marked (ptr); |
| 6874 | mark_localized_symbol (ptr); | 6929 | /* Attempt to catch bogus objects. */ |
| 6875 | break; | 6930 | eassert (valid_lisp_object_p (ptr->u.s.function)); |
| 6876 | case SYMBOL_FORWARDED: | 6931 | mark_stack_push_value (ptr->u.s.function); |
| 6877 | /* If the value is forwarded to a buffer or keyboard field, | 6932 | mark_stack_push_value (ptr->u.s.plist); |
| 6878 | these are marked when we see the corresponding object. | 6933 | switch (ptr->u.s.redirect) |
| 6879 | And if it's forwarded to a C variable, either it's not | 6934 | { |
| 6880 | a Lisp_Object var, or it's staticpro'd already. */ | 6935 | case SYMBOL_PLAINVAL: |
| 6881 | break; | 6936 | mark_stack_push_value (SYMBOL_VAL (ptr)); |
| 6882 | default: emacs_abort (); | 6937 | break; |
| 6938 | case SYMBOL_VARALIAS: | ||
| 6939 | { | ||
| 6940 | Lisp_Object tem; | ||
| 6941 | XSETSYMBOL (tem, SYMBOL_ALIAS (ptr)); | ||
| 6942 | mark_stack_push_value (tem); | ||
| 6943 | break; | ||
| 6944 | } | ||
| 6945 | case SYMBOL_LOCALIZED: | ||
| 6946 | mark_localized_symbol (ptr); | ||
| 6947 | break; | ||
| 6948 | case SYMBOL_FORWARDED: | ||
| 6949 | /* If the value is forwarded to a buffer or keyboard field, | ||
| 6950 | these are marked when we see the corresponding object. | ||
| 6951 | And if it's forwarded to a C variable, either it's not | ||
| 6952 | a Lisp_Object var, or it's staticpro'd already. */ | ||
| 6953 | break; | ||
| 6954 | default: emacs_abort (); | ||
| 6955 | } | ||
| 6956 | if (!PURE_P (XSTRING (ptr->u.s.name))) | ||
| 6957 | set_string_marked (XSTRING (ptr->u.s.name)); | ||
| 6958 | mark_interval_tree (string_intervals (ptr->u.s.name)); | ||
| 6959 | /* Inner loop to mark next symbol in this bucket, if any. */ | ||
| 6960 | po = ptr = ptr->u.s.next; | ||
| 6961 | if (ptr) | ||
| 6962 | goto nextsym; | ||
| 6883 | } | 6963 | } |
| 6884 | if (!PURE_P (XSTRING (ptr->u.s.name))) | ||
| 6885 | set_string_marked (XSTRING (ptr->u.s.name)); | ||
| 6886 | mark_interval_tree (string_intervals (ptr->u.s.name)); | ||
| 6887 | /* Inner loop to mark next symbol in this bucket, if any. */ | ||
| 6888 | po = ptr = ptr->u.s.next; | ||
| 6889 | if (ptr) | ||
| 6890 | goto nextsym; | ||
| 6891 | } | ||
| 6892 | break; | ||
| 6893 | |||
| 6894 | case Lisp_Cons: | ||
| 6895 | { | ||
| 6896 | struct Lisp_Cons *ptr = XCONS (obj); | ||
| 6897 | if (cons_marked_p (ptr)) | ||
| 6898 | break; | 6964 | break; |
| 6899 | CHECK_ALLOCATED_AND_LIVE (live_cons_p, MEM_TYPE_CONS); | 6965 | |
| 6900 | set_cons_marked (ptr); | 6966 | case Lisp_Cons: |
| 6901 | /* If the cdr is nil, avoid recursion for the car. */ | ||
| 6902 | if (NILP (ptr->u.s.u.cdr)) | ||
| 6903 | { | 6967 | { |
| 6968 | struct Lisp_Cons *ptr = XCONS (obj); | ||
| 6969 | if (cons_marked_p (ptr)) | ||
| 6970 | break; | ||
| 6971 | CHECK_ALLOCATED_AND_LIVE (live_cons_p, MEM_TYPE_CONS); | ||
| 6972 | set_cons_marked (ptr); | ||
| 6973 | /* Avoid growing the stack if the cdr is nil. | ||
| 6974 | In any case, make sure the car is expanded first. */ | ||
| 6975 | if (!NILP (ptr->u.s.u.cdr)) | ||
| 6976 | { | ||
| 6977 | mark_stack_push_value (ptr->u.s.u.cdr); | ||
| 6978 | #if GC_CDR_COUNT | ||
| 6979 | cdr_count++; | ||
| 6980 | if (cdr_count == mark_object_loop_halt) | ||
| 6981 | emacs_abort (); | ||
| 6982 | #endif | ||
| 6983 | } | ||
| 6984 | /* Speedup hack for the common case (successive list elements). */ | ||
| 6904 | obj = ptr->u.s.car; | 6985 | obj = ptr->u.s.car; |
| 6905 | cdr_count = 0; | 6986 | goto mark_obj; |
| 6906 | goto loop; | ||
| 6907 | } | 6987 | } |
| 6908 | mark_object (ptr->u.s.car); | ||
| 6909 | obj = ptr->u.s.u.cdr; | ||
| 6910 | cdr_count++; | ||
| 6911 | if (cdr_count == mark_object_loop_halt) | ||
| 6912 | emacs_abort (); | ||
| 6913 | goto loop; | ||
| 6914 | } | ||
| 6915 | 6988 | ||
| 6916 | case Lisp_Float: | 6989 | case Lisp_Float: |
| 6917 | CHECK_ALLOCATED_AND_LIVE (live_float_p, MEM_TYPE_FLOAT); | 6990 | CHECK_ALLOCATED_AND_LIVE (live_float_p, MEM_TYPE_FLOAT); |
| 6918 | /* Do not mark floats stored in a dump image: these floats are | 6991 | /* Do not mark floats stored in a dump image: these floats are |
| 6919 | "cold" and do not have mark bits. */ | 6992 | "cold" and do not have mark bits. */ |
| 6920 | if (pdumper_object_p (XFLOAT (obj))) | 6993 | if (pdumper_object_p (XFLOAT (obj))) |
| 6921 | eassert (pdumper_cold_object_p (XFLOAT (obj))); | 6994 | eassert (pdumper_cold_object_p (XFLOAT (obj))); |
| 6922 | else if (!XFLOAT_MARKED_P (XFLOAT (obj))) | 6995 | else if (!XFLOAT_MARKED_P (XFLOAT (obj))) |
| 6923 | XFLOAT_MARK (XFLOAT (obj)); | 6996 | XFLOAT_MARK (XFLOAT (obj)); |
| 6924 | break; | 6997 | break; |
| 6925 | 6998 | ||
| 6926 | case_Lisp_Int: | 6999 | case_Lisp_Int: |
| 6927 | break; | 7000 | break; |
| 6928 | 7001 | ||
| 6929 | default: | 7002 | default: |
| 6930 | emacs_abort (); | 7003 | emacs_abort (); |
| 7004 | } | ||
| 6931 | } | 7005 | } |
| 6932 | 7006 | ||
| 6933 | #undef CHECK_LIVE | 7007 | #undef CHECK_LIVE |
| @@ -6935,6 +7009,22 @@ mark_object (Lisp_Object arg) | |||
| 6935 | #undef CHECK_ALLOCATED_AND_LIVE | 7009 | #undef CHECK_ALLOCATED_AND_LIVE |
| 6936 | } | 7010 | } |
| 6937 | 7011 | ||
| 7012 | void | ||
| 7013 | mark_object (Lisp_Object obj) | ||
| 7014 | { | ||
| 7015 | ptrdiff_t sp = mark_stk.sp; | ||
| 7016 | mark_stack_push_value (obj); | ||
| 7017 | process_mark_stack (sp); | ||
| 7018 | } | ||
| 7019 | |||
| 7020 | void | ||
| 7021 | mark_objects (Lisp_Object *objs, ptrdiff_t n) | ||
| 7022 | { | ||
| 7023 | ptrdiff_t sp = mark_stk.sp; | ||
| 7024 | mark_stack_push_values (objs, n); | ||
| 7025 | process_mark_stack (sp); | ||
| 7026 | } | ||
| 7027 | |||
| 6938 | /* Mark the Lisp pointers in the terminal objects. | 7028 | /* Mark the Lisp pointers in the terminal objects. |
| 6939 | Called by Fgarbage_collect. */ | 7029 | Called by Fgarbage_collect. */ |
| 6940 | 7030 | ||