diff options
Diffstat (limited to 'src/lisp.h')
-rw-r--r-- | src/lisp.h | 994 |
1 files changed, 647 insertions, 347 deletions
diff --git a/src/lisp.h b/src/lisp.h index 5fa48cec2f0..f066c876619 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -22,15 +22,22 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <alloca.h> #include <setjmp.h> -#include <stdalign.h> #include <stdarg.h> +#include <stdckdint.h> #include <stddef.h> #include <string.h> #include <float.h> #include <inttypes.h> #include <limits.h> +#include <stdio.h> + +#ifdef HAVE_SYS_STAT_H +#include <sys/stat.h> +#endif #include <attribute.h> +#include <byteswap.h> +#include <count-leading-zeros.h> #include <intprops.h> #include <verify.h> @@ -273,7 +280,7 @@ DEFINE_GDB_SYMBOL_END (VALMASK) emacs_align_type union in alloc.c. Although these macros are reasonably portable, they are not - guaranteed on non-GCC platforms, as C11 does not require support + guaranteed on non-GCC platforms, as the C standard does not require support for alignment to GCALIGNMENT and older compilers may ignore alignment requests. For any type T where garbage collection requires alignment, use verify (GCALIGNED (T)) to verify the @@ -297,6 +304,9 @@ DEFINE_GDB_SYMBOL_END (VALMASK) #define LISP_WORDS_ARE_POINTERS (EMACS_INT_MAX == INTPTR_MAX) #if LISP_WORDS_ARE_POINTERS +/* TAG_PTR_INITIALLY casts to Lisp_Word and can be used in static initializers + so this typedef assumes static initializers can contain casts to pointers. + All Emacs targets support this extension to the C standard. */ typedef struct Lisp_X *Lisp_Word; #else typedef EMACS_INT Lisp_Word; @@ -321,7 +331,8 @@ typedef EMACS_INT Lisp_Word; without worrying about the implementations diverging, since lisp_h_OP defines the actual implementation. The lisp_h_OP macros are intended to be private to this include file, and should not be - used elsewhere. + used elsewhere. They should evaluate each argument exactly once, + so that they behave like their functional counterparts. FIXME: Remove the lisp_h_OP macros, and define just the inline OP functions, once "gcc -Og" (new to GCC 4.8) or equivalent works well @@ -363,39 +374,12 @@ typedef EMACS_INT Lisp_Word; # define lisp_h_Qnil {0} #endif -#define lisp_h_PSEUDOVECTORP(a,code) \ - (lisp_h_VECTORLIKEP((a)) && \ - ((XUNTAG ((a), Lisp_Vectorlike, union vectorlike_header)->size \ - & (PSEUDOVECTOR_FLAG | PVEC_TYPE_MASK)) \ - == (PSEUDOVECTOR_FLAG | ((code) << PSEUDOVECTOR_AREA_BITS)))) - #define lisp_h_CHECK_FIXNUM(x) CHECK_TYPE (FIXNUMP (x), Qfixnump, x) #define lisp_h_CHECK_SYMBOL(x) CHECK_TYPE (SYMBOLP (x), Qsymbolp, x) #define lisp_h_CHECK_TYPE(ok, predicate, x) \ ((ok) ? (void) 0 : wrong_type_argument (predicate, x)) #define lisp_h_CONSP(x) TAGGEDP (x, Lisp_Cons) #define lisp_h_BASE_EQ(x, y) (XLI (x) == XLI (y)) -#define lisp_h_BASE2_EQ(x, y) \ - (BASE_EQ (x, y) \ - || (symbols_with_pos_enabled \ - && SYMBOL_WITH_POS_P (x) \ - && BASE_EQ (XSYMBOL_WITH_POS (x)->sym, y))) - -/* FIXME: Do we really need to inline the whole thing? - * What about keeping the part after `symbols_with_pos_enabled` in - * a separate function? */ -#define lisp_h_EQ(x, y) \ - ((XLI ((x)) == XLI ((y))) \ - || (symbols_with_pos_enabled \ - && (SYMBOL_WITH_POS_P ((x)) \ - ? (BARE_SYMBOL_P ((y)) \ - ? XLI (XSYMBOL_WITH_POS((x))->sym) == XLI (y) \ - : SYMBOL_WITH_POS_P((y)) \ - && (XLI (XSYMBOL_WITH_POS((x))->sym) \ - == XLI (XSYMBOL_WITH_POS((y))->sym))) \ - : (SYMBOL_WITH_POS_P ((y)) \ - && BARE_SYMBOL_P ((x)) \ - && (XLI (x) == XLI ((XSYMBOL_WITH_POS ((y)))->sym)))))) #define lisp_h_FIXNUMP(x) \ (! (((unsigned) (XLI (x) >> (USE_LSB_TAG ? 0 : FIXNUM_BITS)) \ @@ -403,18 +387,11 @@ typedef EMACS_INT Lisp_Word; & ((1 << INTTYPEBITS) - 1))) #define lisp_h_FLOATP(x) TAGGEDP (x, Lisp_Float) #define lisp_h_NILP(x) BASE_EQ (x, Qnil) -#define lisp_h_SET_SYMBOL_VAL(sym, v) \ - (eassert ((sym)->u.s.redirect == SYMBOL_PLAINVAL), \ - (sym)->u.s.val.value = (v)) #define lisp_h_SYMBOL_CONSTANT_P(sym) \ (XSYMBOL (sym)->u.s.trapped_write == SYMBOL_NOWRITE) #define lisp_h_SYMBOL_TRAPPED_WRITE_P(sym) (XSYMBOL (sym)->u.s.trapped_write) -#define lisp_h_SYMBOL_VAL(sym) \ - (eassert ((sym)->u.s.redirect == SYMBOL_PLAINVAL), (sym)->u.s.val.value) -#define lisp_h_SYMBOL_WITH_POS_P(x) PSEUDOVECTORP ((x), PVEC_SYMBOL_WITH_POS) -#define lisp_h_BARE_SYMBOL_P(x) TAGGEDP ((x), Lisp_Symbol) -#define lisp_h_SYMBOLP(x) ((BARE_SYMBOL_P ((x)) || \ - (symbols_with_pos_enabled && (SYMBOL_WITH_POS_P ((x)))))) +#define lisp_h_SYMBOL_WITH_POS_P(x) PSEUDOVECTORP (x, PVEC_SYMBOL_WITH_POS) +#define lisp_h_BARE_SYMBOL_P(x) TAGGEDP (x, Lisp_Symbol) #define lisp_h_TAGGEDP(a, tag) \ (! (((unsigned) (XLI (a) >> (USE_LSB_TAG ? 0 : VALBITS)) \ - (unsigned) (tag)) \ @@ -422,8 +399,6 @@ typedef EMACS_INT Lisp_Word; #define lisp_h_VECTORLIKEP(x) TAGGEDP (x, Lisp_Vectorlike) #define lisp_h_XCAR(c) XCONS (c)->u.s.car #define lisp_h_XCDR(c) XCONS (c)->u.s.u.cdr -#define lisp_h_XCONS(a) \ - (eassert (CONSP (a)), XUNTAG (a, Lisp_Cons, struct Lisp_Cons)) #define lisp_h_XHASH(a) XUFIXNUM_RAW (a) #if USE_LSB_TAG # define lisp_h_make_fixnum_wrap(n) \ @@ -465,20 +440,15 @@ typedef EMACS_INT Lisp_Word; # define CHECK_TYPE(ok, predicate, x) lisp_h_CHECK_TYPE (ok, predicate, x) # define CONSP(x) lisp_h_CONSP (x) # define BASE_EQ(x, y) lisp_h_BASE_EQ (x, y) -# define BASE2_EQ(x, y) lisp_h_BASE2_EQ (x, y) # define FLOATP(x) lisp_h_FLOATP (x) # define FIXNUMP(x) lisp_h_FIXNUMP (x) # define NILP(x) lisp_h_NILP (x) -# define SET_SYMBOL_VAL(sym, v) lisp_h_SET_SYMBOL_VAL (sym, v) # define SYMBOL_CONSTANT_P(sym) lisp_h_SYMBOL_CONSTANT_P (sym) # define SYMBOL_TRAPPED_WRITE_P(sym) lisp_h_SYMBOL_TRAPPED_WRITE_P (sym) -# define SYMBOL_VAL(sym) lisp_h_SYMBOL_VAL (sym) -/* # define SYMBOLP(x) lisp_h_SYMBOLP (x) */ /* X is accessed more than once. */ # define TAGGEDP(a, tag) lisp_h_TAGGEDP (a, tag) # define VECTORLIKEP(x) lisp_h_VECTORLIKEP (x) # define XCAR(c) lisp_h_XCAR (c) # define XCDR(c) lisp_h_XCDR (c) -# define XCONS(a) lisp_h_XCONS (a) # define XHASH(a) lisp_h_XHASH (a) # if USE_LSB_TAG # define make_fixnum(n) lisp_h_make_fixnum (n) @@ -509,6 +479,16 @@ typedef EMACS_INT Lisp_Word; #endif +/* Lisp_Object tagging scheme: + Tag location + Upper bits Lower bits Type Payload + 000....... .......000 symbol offset from lispsym to struct Lisp_Symbol + 001....... .......001 unused + 01........ ........10 fixnum signed integer of FIXNUM_BITS + 110....... .......011 cons pointer to struct Lisp_Cons + 100....... .......100 string pointer to struct Lisp_String + 101....... .......101 vectorlike pointer to union vectorlike_header + 111....... .......111 float pointer to struct Lisp_Float */ enum Lisp_Type { /* Symbol. XSYMBOL (object) points to a struct Lisp_Symbol. */ @@ -590,10 +570,8 @@ enum Lisp_Fwd_Type your object -- this way, the same object could be used to represent several disparate C structures. - In addition, you need to add switch branches in data.c for Ftype_of. - - You also need to add the new type to the constant - `cl--typeof-types' in lisp/emacs-lisp/cl-preloaded.el. */ + In addition, you need to add switch branches in data.c for Fcl_type_of + and `cl--define-builtin-type` in lisp/emacs-lisp/cl-preloaded.el. */ /* A Lisp_Object is a tagged pointer or integer. Ordinarily it is a @@ -802,10 +780,11 @@ INLINE void } /* Extract A's pointer value, assuming A's Lisp type is TYPE and the - extracted pointer's type is CTYPE *. */ - -#define XUNTAG(a, type, ctype) ((ctype *) \ - ((char *) XLP (a) - LISP_WORD_TAG (type))) + extracted pointer's type is CTYPE *. When !USE_LSB_TAG this simply + extracts A's low-order bits, as (uintptr_t) LISP_WORD_TAG (type) is + always zero then. */ +#define XUNTAG(a, type, ctype) \ + ((ctype *) ((uintptr_t) XLP (a) - (uintptr_t) LISP_WORD_TAG (type))) /* A forwarding pointer to a value. It uses a generic pointer to avoid alignment bugs that could occur if it used a pointer to a @@ -818,24 +797,24 @@ typedef struct { void const *fwdptr; } lispfwd; enum symbol_interned { - SYMBOL_UNINTERNED = 0, - SYMBOL_INTERNED = 1, - SYMBOL_INTERNED_IN_INITIAL_OBARRAY = 2 + SYMBOL_UNINTERNED, /* not interned anywhere */ + SYMBOL_INTERNED, /* interned but not in initial obarray */ + SYMBOL_INTERNED_IN_INITIAL_OBARRAY /* interned in initial obarray */ }; enum symbol_redirect { - SYMBOL_PLAINVAL = 4, - SYMBOL_VARALIAS = 1, - SYMBOL_LOCALIZED = 2, - SYMBOL_FORWARDED = 3 + SYMBOL_PLAINVAL, /* plain var, value is in the `value' field */ + SYMBOL_VARALIAS, /* var alias, value is really in the `alias' symbol */ + SYMBOL_LOCALIZED, /* localized var, value is in the `blv' object */ + SYMBOL_FORWARDED /* forwarding var, value is in `forward' */ }; enum symbol_trapped_write { - SYMBOL_UNTRAPPED_WRITE = 0, - SYMBOL_NOWRITE = 1, - SYMBOL_TRAPPED_WRITE = 2 + SYMBOL_UNTRAPPED_WRITE, /* normal case, just set the value */ + SYMBOL_NOWRITE, /* constant, cannot set, e.g. nil, t, :keyword */ + SYMBOL_TRAPPED_WRITE /* trap the write, call watcher functions */ }; struct Lisp_Symbol @@ -846,21 +825,13 @@ struct Lisp_Symbol { bool_bf gcmarkbit : 1; - /* Indicates where the value can be found: - 0 : it's a plain var, the value is in the `value' field. - 1 : it's a varalias, the value is really in the `alias' symbol. - 2 : it's a localized var, the value is in the `blv' object. - 3 : it's a forwarding variable, the value is in `forward'. */ - ENUM_BF (symbol_redirect) redirect : 3; + /* Indicates where the value can be found. */ + ENUM_BF (symbol_redirect) redirect : 2; - /* 0 : normal case, just set the value - 1 : constant, cannot set, e.g. nil, t, :keywords. - 2 : trap the write, call watcher functions. */ ENUM_BF (symbol_trapped_write) trapped_write : 2; - /* Interned state of the symbol. This is an enumerator from - enum symbol_interned. */ - unsigned interned : 2; + /* Interned state of the symbol. */ + ENUM_BF (symbol_interned) interned : 2; /* True means that this variable has been explicitly declared special (with `defvar' etc), and shouldn't be lexically bound. */ @@ -920,20 +891,11 @@ verify (GCALIGNED (struct Lisp_Symbol)); #define DEFUN_ARGS_8 (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, \ Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object) -/* untagged_ptr represents a pointer before tagging, and Lisp_Word_tag - contains a possibly-shifted tag to be added to an untagged_ptr to - convert it to a Lisp_Word. */ +/* Lisp_Word_tag is big enough for a possibly-shifted tag, to be + added to a pointer value for conversion to a Lisp_Word. */ #if LISP_WORDS_ARE_POINTERS -/* untagged_ptr is a pointer so that the compiler knows that TAG_PTR - yields a pointer. It is char * so that adding a tag uses simple - machine addition. */ -typedef char *untagged_ptr; typedef uintptr_t Lisp_Word_tag; #else -/* untagged_ptr is an unsigned integer instead of a pointer, so that - it can be added to the possibly-wider Lisp_Word_tag type without - losing information. */ -typedef uintptr_t untagged_ptr; typedef EMACS_UINT Lisp_Word_tag; #endif @@ -941,14 +903,16 @@ typedef EMACS_UINT Lisp_Word_tag; #define LISP_WORD_TAG(tag) \ ((Lisp_Word_tag) (tag) << (USE_LSB_TAG ? 0 : VALBITS)) -/* An initializer for a Lisp_Object that contains TAG along with PTR. */ -#define TAG_PTR(tag, ptr) \ - LISP_INITIALLY ((Lisp_Word) ((untagged_ptr) (ptr) + LISP_WORD_TAG (tag))) +/* An initializer for a Lisp_Object that contains TAG along with P. + P can be a pointer or an integer. The result is usable in a static + initializer if TAG and P are both integer constant expressions. */ +#define TAG_PTR_INITIALLY(tag, p) \ + LISP_INITIALLY ((Lisp_Word) ((uintptr_t) (p) + LISP_WORD_TAG (tag))) /* LISPSYM_INITIALLY (Qfoo) is equivalent to Qfoo except it is - designed for use as an initializer, even for a constant initializer. */ + designed for use as a (possibly static) initializer. */ #define LISPSYM_INITIALLY(name) \ - TAG_PTR (Lisp_Symbol, (char *) (intptr_t) ((i##name) * sizeof *lispsym)) + TAG_PTR_INITIALLY (Lisp_Symbol, (intptr_t) ((i##name) * sizeof *lispsym)) /* Declare extern constants for Lisp symbols. These can be helpful when using a debugger like GDB, on older platforms where the debug @@ -996,25 +960,35 @@ typedef EMACS_UINT Lisp_Word_tag; number of members has been reduced to one. */ union vectorlike_header { - /* The main member contains various pieces of information: - - The MSB (ARRAY_MARK_FLAG) holds the gcmarkbit. - - The next bit (PSEUDOVECTOR_FLAG) indicates whether this is a plain - vector (0) or a pseudovector (1). - - If PSEUDOVECTOR_FLAG is 0, the rest holds the size (number - of slots) of the vector. - - If PSEUDOVECTOR_FLAG is 1, the rest is subdivided into three fields: - - a) pseudovector subtype held in PVEC_TYPE_MASK field; - - b) number of Lisp_Objects slots at the beginning of the object - held in PSEUDOVECTOR_SIZE_MASK field. These objects are always - traced by the GC; - - c) size of the rest fields held in PSEUDOVECTOR_REST_MASK and - measured in word_size units. Rest fields may also include - Lisp_Objects, but these objects usually needs some special treatment - during GC. - There are some exceptions. For PVEC_FREE, b) is always zero. For - PVEC_BOOL_VECTOR and PVEC_SUBR, both b) and c) are always zero. - Current layout limits the pseudovectors to 63 PVEC_xxx subtypes, - 4095 Lisp_Objects in GC-ed area and 4095 word-sized other slots. */ + /* The `size' header word, W bits wide, has one of two forms + discriminated by the second-highest bit (PSEUDOVECTOR_FLAG): + + 1 1 W-2 + +---+---+-------------------------------------+ + | M | 0 | SIZE | vector + +---+---+-------------------------------------+ + + 1 1 W-32 6 12 12 + +---+---+--------+------+----------+----------+ + | M | 1 | unused | TYPE | RESTSIZE | LISPSIZE | pseudovector + +---+---+--------+------+----------+----------+ + + M (ARRAY_MARK_FLAG) holds the GC mark bit. + + SIZE is the length (number of slots) of a regular Lisp vector, + and the object layout is struct Lisp_Vector. + + TYPE is the pseudovector subtype (enum pvec_type). + + LISPSIZE is the number of Lisp_Object fields at the beginning of the + object (after the header). These are always traced by the GC. + + RESTSIZE is the number of fields (in word_size units) following. + These are not automatically traced by the GC. + For PVEC_BOOL and statically allocated PVEC_SUBR, RESTSIZE is 0. + (The block size for PVEC_BOOL is computed from its own size + field, to avoid being restricted by the 12-bit RESTSIZE field.) + */ ptrdiff_t size; }; @@ -1057,6 +1031,7 @@ enum pvec_type PVEC_BOOL_VECTOR, PVEC_BUFFER, PVEC_HASH_TABLE, + PVEC_OBARRAY, PVEC_TERMINAL, PVEC_WINDOW_CONFIGURATION, PVEC_SUBR, @@ -1078,7 +1053,8 @@ enum pvec_type PVEC_CHAR_TABLE, PVEC_SUB_CHAR_TABLE, PVEC_RECORD, - PVEC_FONT /* Should be last because it's used for range checking. */ + PVEC_FONT, + PVEC_TAG_MAX = PVEC_FONT /* Keep this equal to the highest member. */ }; enum More_Lisp_Bits @@ -1115,7 +1091,10 @@ enum More_Lisp_Bits INLINE bool PSEUDOVECTORP (Lisp_Object a, int code) { - return lisp_h_PSEUDOVECTORP (a, code); + return (lisp_h_VECTORLIKEP (a) + && ((XUNTAG (a, Lisp_Vectorlike, union vectorlike_header)->size + & (PSEUDOVECTOR_FLAG | PVEC_TYPE_MASK)) + == (PSEUDOVECTOR_FLAG | (code << PSEUDOVECTOR_AREA_BITS)))); } INLINE bool @@ -1131,9 +1110,10 @@ INLINE bool } INLINE bool -(SYMBOLP) (Lisp_Object x) +SYMBOLP (Lisp_Object x) { - return lisp_h_SYMBOLP (x); + return (BARE_SYMBOL_P (x) + || (symbols_with_pos_enabled && SYMBOL_WITH_POS_P (x))); } INLINE struct Lisp_Symbol_With_Pos * @@ -1143,8 +1123,29 @@ XSYMBOL_WITH_POS (Lisp_Object a) return XUNTAG (a, Lisp_Vectorlike, struct Lisp_Symbol_With_Pos); } +INLINE Lisp_Object +XSYMBOL_WITH_POS_SYM (Lisp_Object a) +{ + Lisp_Object sym = XSYMBOL_WITH_POS (a)->sym; + eassume (BARE_SYMBOL_P (sym)); + return sym; +} + +INLINE Lisp_Object +XSYMBOL_WITH_POS_POS (Lisp_Object a) +{ + return XSYMBOL_WITH_POS (a)->pos; +} + +INLINE Lisp_Object +maybe_remove_pos_from_symbol (Lisp_Object x) +{ + return (symbols_with_pos_enabled && SYMBOL_WITH_POS_P (x) + ? XSYMBOL_WITH_POS_SYM (x) : x); +} + INLINE struct Lisp_Symbol * ATTRIBUTE_NO_SANITIZE_UNDEFINED -(XBARE_SYMBOL) (Lisp_Object a) +XBARE_SYMBOL (Lisp_Object a) { eassert (BARE_SYMBOL_P (a)); intptr_t i = (intptr_t) XUNTAG (a, Lisp_Symbol, struct Lisp_Symbol); @@ -1153,29 +1154,41 @@ INLINE struct Lisp_Symbol * ATTRIBUTE_NO_SANITIZE_UNDEFINED } INLINE struct Lisp_Symbol * ATTRIBUTE_NO_SANITIZE_UNDEFINED -(XSYMBOL) (Lisp_Object a) +XSYMBOL (Lisp_Object a) { - eassert (SYMBOLP ((a))); - if (!symbols_with_pos_enabled || BARE_SYMBOL_P (a)) - return XBARE_SYMBOL (a); - return XBARE_SYMBOL (XSYMBOL_WITH_POS (a)->sym); + if (!BARE_SYMBOL_P (a)) + { + eassume (symbols_with_pos_enabled); + a = XSYMBOL_WITH_POS_SYM (a); + } + return XBARE_SYMBOL (a); } +/* Internal use only. */ INLINE Lisp_Object -make_lisp_symbol (struct Lisp_Symbol *sym) +make_lisp_symbol_internal (struct Lisp_Symbol *sym) { /* GCC 7 x86-64 generates faster code if lispsym is - cast to char * rather than to intptr_t. */ + cast to char * rather than to intptr_t. + Do not use eassert here, so that builtin symbols like Qnil compile to + constants; this is needed for some circa-2024 GCCs even with -O2. */ char *symoffset = (char *) ((char *) sym - (char *) lispsym); - Lisp_Object a = TAG_PTR (Lisp_Symbol, symoffset); - eassert (XSYMBOL (a) == sym); + Lisp_Object a = TAG_PTR_INITIALLY (Lisp_Symbol, symoffset); + return a; +} + +INLINE Lisp_Object +make_lisp_symbol (struct Lisp_Symbol *sym) +{ + Lisp_Object a = make_lisp_symbol_internal (sym); + eassert (XBARE_SYMBOL (a) == sym); return a; } INLINE Lisp_Object builtin_lisp_symbol (int index) { - return make_lisp_symbol (&lispsym[index]); + return make_lisp_symbol_internal (&lispsym[index]); } INLINE bool @@ -1334,20 +1347,15 @@ INLINE bool return lisp_h_BASE_EQ (x, y); } -/* Return true if X and Y are the same object, reckoning X to be the - same as a bare symbol Y if X is Y with position. */ -INLINE bool -(BASE2_EQ) (Lisp_Object x, Lisp_Object y) -{ - return lisp_h_BASE2_EQ (x, y); -} - /* Return true if X and Y are the same object, reckoning a symbol with position as being the same as the bare symbol. */ INLINE bool -(EQ) (Lisp_Object x, Lisp_Object y) +EQ (Lisp_Object x, Lisp_Object y) { - return lisp_h_EQ (x, y); + return BASE_EQ ((symbols_with_pos_enabled && SYMBOL_WITH_POS_P (x) + ? XSYMBOL_WITH_POS_SYM (x) : x), + (symbols_with_pos_enabled && SYMBOL_WITH_POS_P (y) + ? XSYMBOL_WITH_POS_SYM (y) : y)); } INLINE intmax_t @@ -1361,7 +1369,7 @@ clip_to_bounds (intmax_t lower, intmax_t num, intmax_t upper) INLINE Lisp_Object make_lisp_ptr (void *ptr, enum Lisp_Type type) { - Lisp_Object a = TAG_PTR (type, ptr); + Lisp_Object a = TAG_PTR_INITIALLY (type, ptr); eassert (TAGGEDP (a, type) && XUNTAG (a, type, char) == ptr); return a; } @@ -1406,19 +1414,19 @@ dead_object (void) == (PSEUDOVECTOR_FLAG | (code << PSEUDOVECTOR_AREA_BITS)))) #define XSETWINDOW_CONFIGURATION(a, b) \ - (XSETPSEUDOVECTOR (a, b, PVEC_WINDOW_CONFIGURATION)) -#define XSETPROCESS(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_PROCESS)) -#define XSETWINDOW(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_WINDOW)) -#define XSETTERMINAL(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_TERMINAL)) -#define XSETSUBR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_SUBR)) -#define XSETBUFFER(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_BUFFER)) -#define XSETCHAR_TABLE(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CHAR_TABLE)) -#define XSETBOOL_VECTOR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_BOOL_VECTOR)) -#define XSETSUB_CHAR_TABLE(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_SUB_CHAR_TABLE)) -#define XSETTHREAD(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_THREAD)) -#define XSETMUTEX(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_MUTEX)) -#define XSETCONDVAR(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_CONDVAR)) -#define XSETNATIVE_COMP_UNIT(a, b) (XSETPSEUDOVECTOR (a, b, PVEC_NATIVE_COMP_UNIT)) + XSETPSEUDOVECTOR (a, b, PVEC_WINDOW_CONFIGURATION) +#define XSETPROCESS(a, b) XSETPSEUDOVECTOR (a, b, PVEC_PROCESS) +#define XSETWINDOW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_WINDOW) +#define XSETTERMINAL(a, b) XSETPSEUDOVECTOR (a, b, PVEC_TERMINAL) +#define XSETSUBR(a, b) XSETPSEUDOVECTOR (a, b, PVEC_SUBR) +#define XSETBUFFER(a, b) XSETPSEUDOVECTOR (a, b, PVEC_BUFFER) +#define XSETCHAR_TABLE(a, b) XSETPSEUDOVECTOR (a, b, PVEC_CHAR_TABLE) +#define XSETBOOL_VECTOR(a, b) XSETPSEUDOVECTOR (a, b, PVEC_BOOL_VECTOR) +#define XSETSUB_CHAR_TABLE(a, b) XSETPSEUDOVECTOR (a, b, PVEC_SUB_CHAR_TABLE) +#define XSETTHREAD(a, b) XSETPSEUDOVECTOR (a, b, PVEC_THREAD) +#define XSETMUTEX(a, b) XSETPSEUDOVECTOR (a, b, PVEC_MUTEX) +#define XSETCONDVAR(a, b) XSETPSEUDOVECTOR (a, b, PVEC_CONDVAR) +#define XSETNATIVE_COMP_UNIT(a, b) XSETPSEUDOVECTOR (a, b, PVEC_NATIVE_COMP_UNIT) /* Efficiently convert a pointer to a Lisp object and back. The pointer is represented as a fixnum, so the garbage collector @@ -1434,7 +1442,7 @@ XFIXNUMPTR (Lisp_Object a) INLINE Lisp_Object make_pointer_integer_unsafe (void *p) { - Lisp_Object a = TAG_PTR (Lisp_Int0, p); + Lisp_Object a = TAG_PTR_INITIALLY (Lisp_Int0, p); return a; } @@ -1492,9 +1500,10 @@ CHECK_CONS (Lisp_Object x) } INLINE struct Lisp_Cons * -(XCONS) (Lisp_Object a) +XCONS (Lisp_Object a) { - return lisp_h_XCONS (a); + eassert (CONSP (a)); + return XUNTAG (a, Lisp_Cons, struct Lisp_Cons); } /* Take the car or cdr of something known to be a cons cell. */ @@ -1874,6 +1883,30 @@ bool_vector_bytes (EMACS_INT size) return (size + BOOL_VECTOR_BITS_PER_CHAR - 1) / BOOL_VECTOR_BITS_PER_CHAR; } +INLINE bits_word +bits_word_to_host_endian (bits_word val) +{ +#ifndef WORDS_BIGENDIAN + return val; +#else + if (BITS_WORD_MAX >> 31 == 1) + return bswap_32 (val); + if (BITS_WORD_MAX >> 31 >> 31 >> 1 == 1) + return bswap_64 (val); + { + int i; + bits_word r = 0; + for (i = 0; i < sizeof val; i++) + { + r = ((r << 1 << (CHAR_BIT - 1)) + | (val & ((1u << 1 << (CHAR_BIT - 1)) - 1))); + val = val >> 1 >> (CHAR_BIT - 1); + } + return r; + } +#endif +} + INLINE bool BOOL_VECTOR_P (Lisp_Object a) { @@ -2025,9 +2058,7 @@ ASCII_CHAR_P (intmax_t c) range of characters. A sub-char-table is like a vector, but with two integer fields between the header and Lisp data, which means that it has to be marked with some precautions (see mark_char_table - in alloc.c). A sub-char-table appears only in an element of a - char-table, and there's no way to access it directly from a Lisp - program. */ + in alloc.c). A sub-char-table appears in an element of a char-table. */ enum CHARTAB_SIZE_BITS { @@ -2281,9 +2312,10 @@ typedef jmp_buf sys_jmp_buf; /* Value is name of symbol. */ INLINE Lisp_Object -(SYMBOL_VAL) (struct Lisp_Symbol *sym) +SYMBOL_VAL (struct Lisp_Symbol *sym) { - return lisp_h_SYMBOL_VAL (sym); + eassert (sym->u.s.redirect == SYMBOL_PLAINVAL); + return sym->u.s.val.value; } INLINE struct Lisp_Symbol * @@ -2306,9 +2338,10 @@ SYMBOL_FWD (struct Lisp_Symbol *sym) } INLINE void -(SET_SYMBOL_VAL) (struct Lisp_Symbol *sym, Lisp_Object v) +SET_SYMBOL_VAL (struct Lisp_Symbol *sym, Lisp_Object v) { - lisp_h_SET_SYMBOL_VAL (sym, v); + eassert (sym->u.s.redirect == SYMBOL_PLAINVAL); + sym->u.s.val.value = v; } INLINE void @@ -2377,6 +2410,118 @@ INLINE int definition is done by lread.c's define_symbol. */ #define DEFSYM(sym, name) /* empty */ + +struct Lisp_Obarray +{ + union vectorlike_header header; + + /* Array of 2**size_bits values, each being either a (bare) symbol or + the fixnum 0. The symbols for each bucket are chained via + their s.next field. */ + Lisp_Object *buckets; + + unsigned size_bits; /* log2(size of buckets vector) */ + unsigned count; /* number of symbols in obarray */ +}; + +INLINE bool +OBARRAYP (Lisp_Object a) +{ + return PSEUDOVECTORP (a, PVEC_OBARRAY); +} + +INLINE struct Lisp_Obarray * +XOBARRAY (Lisp_Object a) +{ + eassert (OBARRAYP (a)); + return XUNTAG (a, Lisp_Vectorlike, struct Lisp_Obarray); +} + +INLINE void +CHECK_OBARRAY (Lisp_Object x) +{ + CHECK_TYPE (OBARRAYP (x), Qobarrayp, x); +} + +INLINE Lisp_Object +make_lisp_obarray (struct Lisp_Obarray *o) +{ + eassert (PSEUDOVECTOR_TYPEP (&o->header, PVEC_OBARRAY)); + return make_lisp_ptr (o, Lisp_Vectorlike); +} + +INLINE ptrdiff_t +obarray_size (const struct Lisp_Obarray *o) +{ + return (ptrdiff_t)1 << o->size_bits; +} + +Lisp_Object check_obarray_slow (Lisp_Object); + +/* Return an obarray object from OBARRAY or signal an error. */ +INLINE Lisp_Object +check_obarray (Lisp_Object obarray) +{ + return OBARRAYP (obarray) ? obarray : check_obarray_slow (obarray); +} + +/* Obarray iterator state. Don't access these members directly. + The iterator functions must be called in the order followed by DOOBARRAY. */ +typedef struct { + struct Lisp_Obarray *o; + ptrdiff_t idx; /* Current bucket index. */ + struct Lisp_Symbol *symbol; /* Current symbol, or NULL if at end + of current bucket. */ +} obarray_iter_t; + +INLINE obarray_iter_t +make_obarray_iter (struct Lisp_Obarray *oa) +{ + return (obarray_iter_t){.o = oa, .idx = -1, .symbol = NULL}; +} + +/* Whether IT has reached the end and there are no more symbols. + If true, IT is dead and cannot be used any more. */ +INLINE bool +obarray_iter_at_end (obarray_iter_t *it) +{ + if (it->symbol) + return false; + ptrdiff_t size = obarray_size (it->o); + while (++it->idx < size) + { + Lisp_Object obj = it->o->buckets[it->idx]; + if (!BASE_EQ (obj, make_fixnum (0))) + { + it->symbol = XBARE_SYMBOL (obj); + return false; + } + } + return true; +} + +/* Advance IT to the next symbol if any. */ +INLINE void +obarray_iter_step (obarray_iter_t *it) +{ + it->symbol = it->symbol->u.s.next; +} + +/* The Lisp symbol at IT, if obarray_iter_at_end returned false. */ +INLINE Lisp_Object +obarray_iter_symbol (obarray_iter_t *it) +{ + return make_lisp_symbol (it->symbol); +} + +/* Iterate IT over the symbols of the obarray OA. + The body shouldn't add or remove symbols in OA, but disobeying that rule + only risks symbols to be iterated more than once or not at all, + not crashes or data corruption. */ +#define DOOBARRAY(oa, it) \ + for (obarray_iter_t it = make_obarray_iter (oa); \ + !obarray_iter_at_end (&it); obarray_iter_step (&it)) + /*********************************************************************** Hash Tables @@ -2386,10 +2531,23 @@ INLINE int struct Lisp_Hash_Table; +/* The type of a hash value stored in the table. + It's unsigned and a subtype of EMACS_UINT. */ +typedef uint32_t hash_hash_t; + +typedef enum { + Test_eql, + Test_eq, + Test_equal, +} hash_table_std_test_t; + struct hash_table_test { - /* Name of the function used to compare keys. */ - Lisp_Object name; + /* C function to compute hash code. */ + hash_hash_t (*hashfn) (Lisp_Object, struct Lisp_Hash_Table *); + + /* C function to compare two keys. */ + Lisp_Object (*cmpfn) (Lisp_Object, Lisp_Object, struct Lisp_Hash_Table *); /* User-supplied hash function, or nil. */ Lisp_Object user_hash_function; @@ -2397,78 +2555,109 @@ struct hash_table_test /* User-supplied key comparison function, or nil. */ Lisp_Object user_cmp_function; - /* C function to compare two keys. */ - Lisp_Object (*cmpfn) (Lisp_Object, Lisp_Object, struct Lisp_Hash_Table *); - - /* C function to compute hash code. */ - Lisp_Object (*hashfn) (Lisp_Object, struct Lisp_Hash_Table *); + /* Function used to compare keys; always a bare symbol. */ + Lisp_Object name; }; +typedef enum { + Weak_None, /* No weak references. */ + Weak_Key, /* Reference to key is weak. */ + Weak_Value, /* Reference to value is weak. */ + Weak_Key_Or_Value, /* References to key or value are weak: + element kept as long as strong reference to + either key or value remains. */ + Weak_Key_And_Value, /* References to key and value are weak: + element kept as long as strong references to + both key and value remain. */ +} hash_table_weakness_t; + +/* The type of a hash table index, both for table indices and index + (hash) indices. It's signed and a subtype of ptrdiff_t. */ +typedef int32_t hash_idx_t; + struct Lisp_Hash_Table { - /* Change pdumper.c if you change the fields here. */ - - /* This is for Lisp; the hash table code does not refer to it. */ union vectorlike_header header; - /* Nil if table is non-weak. Otherwise a symbol describing the - weakness of the table. */ - Lisp_Object weak; + /* Hash table internal structure: + + Lisp key index table + | vector + | hash fn hash key value next + v +--+ +------+-------+------+----+ + hash value |-1| | C351 | cow | moo | -1 |<- + | +--+ +------+-------+------+----+ | + ------------>| -------->| 07A8 | cat | meow | -1 | | + range +--+ +------+-------+------+----+ | + reduction |-1| ->| 91D2 | dog | woof | ---- + +--+ | +------+-------+------+----+ + | ------ | ? |unbound| ? | -1 |<- + +--+ +------+-------+------+----+ | + | -------->| F6B0 | duck |quack | -1 | | + +--+ +------+-------+------+----+ | + |-1| ->| ? |unbound| ? | ---- + +--+ | +------+-------+------+----+ + : : | : : : : : + | + next_free + + The table is physically split into three vectors (hash, next, + key_and_value) which may or may not be beneficial. */ + + /* Bucket vector. An entry of -1 indicates no item is present, + and a nonnegative entry is the index of the first item in + a collision chain. + This vector is 2**index_bits entries long. + If index_bits is 0 (and table_size is 0), then this is the + constant read-only vector {-1}, shared between all instances. + Otherwise it is heap-allocated. */ + hash_idx_t *index; + + /* Vector of hash codes. Unused entries have undefined values. + This vector is table_size entries long. */ + hash_hash_t *hash; - /* Vector of hash codes, or nil if the table needs rehashing. - If the I-th entry is unused, then hash[I] should be nil. */ - Lisp_Object hash; + /* Vector of keys and values. The key of item I is found at index + 2 * I, the value is found at index 2 * I + 1. + If the key is HASH_UNUSED_ENTRY_KEY, then this slot is unused. + This is gc_marked specially if the table is weak. + This vector is 2 * table_size entries long. */ + Lisp_Object *key_and_value; + + /* The comparison and hash functions. */ + const struct hash_table_test *test; /* Vector used to chain entries. If entry I is free, next[I] is the entry number of the next free item. If entry I is non-free, next[I] is the index of the next entry in the collision chain, - or -1 if there is such entry. */ - Lisp_Object next; - - /* Bucket vector. An entry of -1 indicates no item is present, - and a nonnegative entry is the index of the first item in - a collision chain. This vector's size can be larger than the - hash table size to reduce collisions. */ - Lisp_Object index; - - /* Only the fields above are traced normally by the GC. The ones after - 'index' are special and are either ignored by the GC or traced in - a special way (e.g. because of weakness). */ + or -1 if there is no such entry. + This vector is table_size entries long. */ + hash_idx_t *next; /* Number of key/value entries in the table. */ - ptrdiff_t count; + hash_idx_t count; /* Index of first free entry in free list, or -1 if none. */ - ptrdiff_t next_free; + hash_idx_t next_free; + + hash_idx_t table_size; /* Size of the next and hash vectors. */ + + unsigned char index_bits; /* log2 (size of the index vector). */ + + /* Weakness of the table. */ + hash_table_weakness_t weakness : 3; + + /* Hash table test (only used when frozen in dump) */ + hash_table_std_test_t frozen_test : 2; /* True if the table can be purecopied. The table cannot be changed afterwards. */ - bool purecopy; + bool_bf purecopy : 1; /* True if the table is mutable. Ordinarily tables are mutable, but pure tables are not, and while a table is being mutated it is immutable for recursive attempts to mutate it. */ - bool mutable; - - /* Resize hash table when number of entries / table size is >= this - ratio. */ - float rehash_threshold; - - /* Used when the table is resized. If equal to a negative integer, - the user rehash-size is the integer -REHASH_SIZE, and the new - size is the old size plus -REHASH_SIZE. If positive, the user - rehash-size is the floating-point value REHASH_SIZE + 1, and the - new size is the old size times REHASH_SIZE + 1. */ - float rehash_size; - - /* Vector of keys and values. The key of item I is found at index - 2 * I, the value is found at index 2 * I + 1. - If the key is equal to Qunbound, then this slot is unused. - This is gc_marked specially if the table is weak. */ - Lisp_Object key_and_value; - - /* The comparison and hash functions. */ - struct hash_table_test test; + bool_bf mutable : 1; /* Next weak hash table if this is a weak hash table. The head of the list is in weak_hash_tables. Used only during garbage @@ -2476,8 +2665,20 @@ struct Lisp_Hash_Table struct Lisp_Hash_Table *next_weak; } GCALIGNED_STRUCT; -/* Sanity-check pseudovector layout. */ -verify (offsetof (struct Lisp_Hash_Table, weak) == header_size); +/* A specific Lisp_Object that is not a valid Lisp value. + We need to be careful not to leak this value into machinery + where it may be treated as one; we'd get a segfault if lucky. */ +#define INVALID_LISP_VALUE make_lisp_ptr (NULL, Lisp_Float) + +/* Key value that marks an unused hash table entry. */ +#define HASH_UNUSED_ENTRY_KEY INVALID_LISP_VALUE + +/* KEY is a key of an unused hash table entry. */ +INLINE bool +hash_unused_entry_key_p (Lisp_Object key) +{ + return BASE_EQ (key, HASH_UNUSED_ENTRY_KEY); +} INLINE bool HASH_TABLE_P (Lisp_Object a) @@ -2492,54 +2693,97 @@ XHASH_TABLE (Lisp_Object a) return XUNTAG (a, Lisp_Vectorlike, struct Lisp_Hash_Table); } -#define XSET_HASH_TABLE(VAR, PTR) \ - (XSETPSEUDOVECTOR (VAR, PTR, PVEC_HASH_TABLE)) +INLINE Lisp_Object +make_lisp_hash_table (struct Lisp_Hash_Table *h) +{ + eassert (PSEUDOVECTOR_TYPEP (&h->header, PVEC_HASH_TABLE)); + return make_lisp_ptr (h, Lisp_Vectorlike); +} /* Value is the key part of entry IDX in hash table H. */ INLINE Lisp_Object HASH_KEY (const struct Lisp_Hash_Table *h, ptrdiff_t idx) { - return AREF (h->key_and_value, 2 * idx); + eassert (idx >= 0 && idx < h->table_size); + return h->key_and_value[2 * idx]; } /* Value is the value part of entry IDX in hash table H. */ INLINE Lisp_Object HASH_VALUE (const struct Lisp_Hash_Table *h, ptrdiff_t idx) { - return AREF (h->key_and_value, 2 * idx + 1); + eassert (idx >= 0 && idx < h->table_size); + return h->key_and_value[2 * idx + 1]; } /* Value is the hash code computed for entry IDX in hash table H. */ -INLINE Lisp_Object +INLINE hash_hash_t HASH_HASH (const struct Lisp_Hash_Table *h, ptrdiff_t idx) { - return AREF (h->hash, idx); + eassert (idx >= 0 && idx < h->table_size); + return h->hash[idx]; } /* Value is the size of hash table H. */ INLINE ptrdiff_t HASH_TABLE_SIZE (const struct Lisp_Hash_Table *h) { - ptrdiff_t size = ASIZE (h->next); - eassume (0 < size); - return size; + return h->table_size; } -void hash_table_rehash (Lisp_Object); +/* Size of the index vector in hash table H. */ +INLINE ptrdiff_t +hash_table_index_size (const struct Lisp_Hash_Table *h) +{ + return (ptrdiff_t)1 << h->index_bits; +} + +/* Hash value for KEY in hash table H. */ +INLINE hash_hash_t +hash_from_key (struct Lisp_Hash_Table *h, Lisp_Object key) +{ + return h->test->hashfn (key, h); +} + +/* Iterate K and V as key and value of valid entries in hash table H. + The body may remove the current entry or alter its value slot, but not + mutate TABLE in any other way. */ +#define DOHASH(h, k, v) \ + for (Lisp_Object *dohash_##k##_##v##_kv = (h)->key_and_value, \ + *dohash_##k##_##v##_end = dohash_##k##_##v##_kv \ + + 2 * HASH_TABLE_SIZE (h), \ + *dohash_##k##_##v##_base = dohash_##k##_##v##_kv, \ + k, v; \ + dohash_##k##_##v##_kv < dohash_##k##_##v##_end \ + && (k = dohash_##k##_##v##_kv[0], \ + v = dohash_##k##_##v##_kv[1], /*maybe unused*/ (void)v, \ + true); \ + eassert (dohash_##k##_##v##_base == (h)->key_and_value \ + && dohash_##k##_##v##_end \ + == dohash_##k##_##v##_base \ + + 2 * HASH_TABLE_SIZE (h)), \ + dohash_##k##_##v##_kv += 2) \ + if (hash_unused_entry_key_p (k)) \ + ; \ + else + +/* Iterate I as index of valid entries in hash table H. + Unlike DOHASH, this construct copes with arbitrary table mutations + in the body. The consequences of such mutations are limited to + whether and in what order entries are encountered by the loop + (which is usually bad enough), but not crashing or corrupting the + Lisp state. */ +#define DOHASH_SAFE(h, i) \ + for (ptrdiff_t i = 0; i < HASH_TABLE_SIZE (h); i++) \ + if (hash_unused_entry_key_p (HASH_KEY (h, i))) \ + ; \ + else + +void hash_table_thaw (Lisp_Object hash_table); /* Default size for hash tables if not specified. */ -enum DEFAULT_HASH_SIZE { DEFAULT_HASH_SIZE = 65 }; - -/* Default threshold specifying when to resize a hash table. The - value gives the ratio of current entries in the hash table and the - size of the hash table. */ - -static float const DEFAULT_REHASH_THRESHOLD = 0.8125; - -/* Default factor by which to increase the size of a hash table, minus 1. */ - -static float const DEFAULT_REHASH_SIZE = 1.5 - 1; +enum DEFAULT_HASH_SIZE { DEFAULT_HASH_SIZE = 0 }; /* Combine two integers X and Y for hashing. The result might exceed INTMASK. */ @@ -2558,6 +2802,28 @@ SXHASH_REDUCE (EMACS_UINT x) return (x ^ x >> (EMACS_INT_WIDTH - FIXNUM_BITS)) & INTMASK; } +/* Reduce an EMACS_UINT hash value to hash_hash_t. */ +INLINE hash_hash_t +reduce_emacs_uint_to_hash_hash (EMACS_UINT x) +{ + verify (sizeof x <= 2 * sizeof (hash_hash_t)); + return (sizeof x == sizeof (hash_hash_t) + ? x + : x ^ (x >> (8 * (sizeof x - sizeof (hash_hash_t))))); +} + +/* Reduce HASH to a value BITS wide. */ +INLINE ptrdiff_t +knuth_hash (hash_hash_t hash, unsigned bits) +{ + /* Knuth multiplicative hashing, tailored for 32-bit indices + (avoiding a 64-bit multiply). */ + uint32_t alpha = 2654435769; /* 2**32/phi */ + /* Note the cast to uint64_t, to make it work for bits=0. */ + return (uint64_t)((uint32_t)hash * alpha) >> (32 - bits); +} + + struct Lisp_Marker { union vectorlike_header header; @@ -2642,7 +2908,7 @@ extern Lisp_Object make_misc_ptr (void *); INLINE Lisp_Object make_mint_ptr (void *a) { - Lisp_Object val = TAG_PTR (Lisp_Int0, a); + Lisp_Object val = TAG_PTR_INITIALLY (Lisp_Int0, a); return FIXNUMP (val) && XFIXNUMPTR (val) == a ? val : make_misc_ptr (a); } @@ -2736,22 +3002,6 @@ XOVERLAY (Lisp_Object a) return XUNTAG (a, Lisp_Vectorlike, struct Lisp_Overlay); } -INLINE Lisp_Object -SYMBOL_WITH_POS_SYM (Lisp_Object a) -{ - if (!SYMBOL_WITH_POS_P (a)) - wrong_type_argument (Qsymbol_with_pos_p, a); - return XSYMBOL_WITH_POS (a)->sym; -} - -INLINE Lisp_Object -SYMBOL_WITH_POS_POS (Lisp_Object a) -{ - if (!SYMBOL_WITH_POS_P (a)) - wrong_type_argument (Qsymbol_with_pos_p, a); - return XSYMBOL_WITH_POS (a)->pos; -} - INLINE bool USER_PTRP (Lisp_Object x) { @@ -2964,9 +3214,10 @@ XFLOAT_DATA (Lisp_Object f) /* Most hosts nowadays use IEEE floating point, so they use IEC 60559 representations, have infinities and NaNs, and do not trap on exceptions. Define IEEE_FLOATING_POINT to 1 if this host is one of the - typical ones. The C11 macro __STDC_IEC_559__ is close to what is + typical ones. The C23 macro __STDC_IEC_60559_BFP__ (or its + obsolescent C11 counterpart __STDC_IEC_559__) is close to what is wanted here, but is not quite right because Emacs does not require - all the features of C11 Annex F (and does not require C11 at all, + all the features of C23 Annex F (and does not require C11 or later, for that matter). */ #define IEEE_FLOATING_POINT (FLT_RADIX == 2 && FLT_MANT_DIG == 24 \ @@ -3233,77 +3484,25 @@ enum maxargs empty initializers), and is overkill for simple usages like 'Finsert (1, &text);'. */ #define CALLN(f, ...) CALLMANY (f, ((Lisp_Object []) {__VA_ARGS__})) - -/* Call function fn on no arguments. */ +#define calln(...) CALLN (Ffuncall, __VA_ARGS__) +/* Compatibility aliases. */ +#define call1 calln +#define call2 calln +#define call3 calln +#define call4 calln +#define call5 calln +#define call6 calln +#define call7 calln +#define call8 calln + +/* Define 'call0' as a function rather than a CPP macro because we + sometimes want to pass it as a first class function. */ INLINE Lisp_Object call0 (Lisp_Object fn) { return Ffuncall (1, &fn); } -/* Call function fn with 1 argument arg1. */ -INLINE Lisp_Object -call1 (Lisp_Object fn, Lisp_Object arg1) -{ - return CALLN (Ffuncall, fn, arg1); -} - -/* Call function fn with 2 arguments arg1, arg2. */ -INLINE Lisp_Object -call2 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2) -{ - return CALLN (Ffuncall, fn, arg1, arg2); -} - -/* Call function fn with 3 arguments arg1, arg2, arg3. */ -INLINE Lisp_Object -call3 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3); -} - -/* Call function fn with 4 arguments arg1, arg2, arg3, arg4. */ -INLINE Lisp_Object -call4 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3, - Lisp_Object arg4) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3, arg4); -} - -/* Call function fn with 5 arguments arg1, arg2, arg3, arg4, arg5. */ -INLINE Lisp_Object -call5 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3, - Lisp_Object arg4, Lisp_Object arg5) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3, arg4, arg5); -} - -/* Call function fn with 6 arguments arg1, arg2, arg3, arg4, arg5, arg6. */ -INLINE Lisp_Object -call6 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3, - Lisp_Object arg4, Lisp_Object arg5, Lisp_Object arg6) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3, arg4, arg5, arg6); -} - -/* Call function fn with 7 arguments arg1, arg2, arg3, arg4, arg5, arg6, arg7. */ -INLINE Lisp_Object -call7 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3, - Lisp_Object arg4, Lisp_Object arg5, Lisp_Object arg6, Lisp_Object arg7) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3, arg4, arg5, arg6, arg7); -} - -/* Call function fn with 8 arguments arg1, arg2, arg3, arg4, arg5, - arg6, arg7, arg8. */ -INLINE Lisp_Object -call8 (Lisp_Object fn, Lisp_Object arg1, Lisp_Object arg2, Lisp_Object arg3, - Lisp_Object arg4, Lisp_Object arg5, Lisp_Object arg6, Lisp_Object arg7, - Lisp_Object arg8) -{ - return CALLN (Ffuncall, fn, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); -} - extern void defvar_lisp (struct Lisp_Objfwd const *, char const *); extern void defvar_lisp_nopro (struct Lisp_Objfwd const *, char const *); extern void defvar_bool (struct Lisp_Boolfwd const *, char const *); @@ -3595,7 +3794,8 @@ record_in_backtrace (Lisp_Object function, Lisp_Object *args, ptrdiff_t nargs) } /* This structure helps implement the `catch/throw' and `condition-case/signal' - control structures. A struct handler contains all the information needed to + control structures as well as 'handler-bind'. + A struct handler contains all the information needed to restore the state of the interpreter after a non-local jump. Handler structures are chained together in a doubly linked list; the `next' @@ -3616,9 +3816,41 @@ record_in_backtrace (Lisp_Object function, Lisp_Object *args, ptrdiff_t nargs) state. Members are volatile if their values need to survive _longjmp when - a 'struct handler' is a local variable. */ - -enum handlertype { CATCHER, CONDITION_CASE, CATCHER_ALL }; + a 'struct handler' is a local variable. + + When running the HANDLER of a 'handler-bind', we need to + temporarily "mute" the CONDITION_CASEs and HANDLERs that are "below" + the current handler, but without hiding any CATCHERs. We do that by + installing a SKIP_CONDITIONS which tells the search to skip the + N next conditions. */ + +enum handlertype { + CATCHER, /* Entry for 'catch'. + 'tag_or_ch' holds the catch's tag. + 'val' holds the retval during longjmp. */ + CONDITION_CASE, /* Entry for 'condition-case'. + 'tag_or_ch' holds the list of conditions. + 'val' holds the retval during longjmp. */ + CATCHER_ALL, /* Wildcard which catches all 'throw's. + 'tag_or_ch' is unused. + 'val' holds the retval during longjmp. */ + HANDLER_BIND, /* Entry for 'handler-bind'. + 'tag_or_ch' holds the list of conditions. + 'val' holds the handler function. + The rest of the handler is unused, + except for 'bytecode_dest' that holds + the number of preceding HANDLER_BIND + entries which belong to the same + 'handler-bind' (and hence need to + be muted together). */ + SKIP_CONDITIONS /* Mask out the N preceding entries. + Used while running the handler of + a HANDLER_BIND to hides the condition + handlers underneath (and including) + the 'handler-bind'. + 'tag_or_ch' holds that number, the rest + is unused. */ +}; enum nonlocal_exit { @@ -3764,13 +3996,15 @@ vcopy (Lisp_Object v, ptrdiff_t offset, Lisp_Object const *args, INLINE void set_hash_key_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) { - gc_aset (h->key_and_value, 2 * idx, val); + eassert (idx >= 0 && idx < h->table_size); + h->key_and_value[2 * idx] = val; } INLINE void set_hash_value_slot (struct Lisp_Hash_Table *h, ptrdiff_t idx, Lisp_Object val) { - gc_aset (h->key_and_value, 2 * idx + 1, val); + eassert (idx >= 0 && idx < h->table_size); + h->key_and_value[2 * idx + 1] = val;; } /* Use these functions to set Lisp_Object @@ -3913,6 +4147,13 @@ integer_to_uintmax (Lisp_Object num, uintmax_t *n) } } +/* Return floor (log2 (N)) as an int, where 0 < N <= ULLONG_MAX. */ +INLINE int +elogb (unsigned long long int n) +{ + return ULLONG_WIDTH - 1 - count_leading_zeros_ll (n); +} + /* A modification count. These are wide enough, and incremented rarely enough, so that they should never overflow a 60-bit counter in practice, and the code below assumes this so a compiler can @@ -3922,12 +4163,13 @@ typedef intmax_t modiff_count; INLINE modiff_count modiff_incr (modiff_count *a, ptrdiff_t len) { - modiff_count a0 = *a; int incr = len ? 1 : 0; + modiff_count a0 = *a; /* Increase the counter more for a large modification and less for a small modification. Increase it logarithmically to avoid increasing it too much. */ - while (len >>= 1) incr++; - bool modiff_overflow = INT_ADD_WRAPV (a0, incr, a); + verify (PTRDIFF_MAX <= ULLONG_MAX); + int incr = len == 0 ? 1 : elogb (len) + 1; + bool modiff_overflow = ckd_add (a, a0, incr); eassert (!modiff_overflow && *a >> 30 >> 30 == 0); return a0; } @@ -3966,7 +4208,6 @@ extern Lisp_Object arithcompare (Lisp_Object num1, Lisp_Object num2, extern intmax_t cons_to_signed (Lisp_Object, intmax_t, intmax_t); extern uintmax_t cons_to_unsigned (Lisp_Object, uintmax_t); -extern struct Lisp_Symbol *indirect_variable (struct Lisp_Symbol *); extern AVOID args_out_of_range (Lisp_Object, Lisp_Object); extern AVOID circular_list (Lisp_Object); extern Lisp_Object do_symval_forwarding (lispfwd); @@ -4001,6 +4242,7 @@ extern ptrdiff_t multibyte_chars_in_text (const unsigned char *, ptrdiff_t); extern void syms_of_character (void); /* Defined in charset.c. */ +extern void mark_charset (void); extern void init_charset (void); extern void init_charset_once (void); extern void syms_of_charset (void); @@ -4021,12 +4263,14 @@ extern void hexbuf_digest (char *, void const *, int); extern char *extract_data_from_object (Lisp_Object, ptrdiff_t *, ptrdiff_t *); EMACS_UINT hash_string (char const *, ptrdiff_t); EMACS_UINT sxhash (Lisp_Object); -Lisp_Object hashfn_user_defined (Lisp_Object, struct Lisp_Hash_Table *); -Lisp_Object make_hash_table (struct hash_table_test, EMACS_INT, float, float, - Lisp_Object, bool); -ptrdiff_t hash_lookup (struct Lisp_Hash_Table *, Lisp_Object, Lisp_Object *); +Lisp_Object make_hash_table (const struct hash_table_test *, EMACS_INT, + hash_table_weakness_t, bool); +Lisp_Object hash_table_weakness_symbol (hash_table_weakness_t weak); +ptrdiff_t hash_lookup (struct Lisp_Hash_Table *, Lisp_Object); +ptrdiff_t hash_lookup_get_hash (struct Lisp_Hash_Table *h, Lisp_Object key, + hash_hash_t *phash); ptrdiff_t hash_put (struct Lisp_Hash_Table *, Lisp_Object, Lisp_Object, - Lisp_Object); + hash_hash_t); void hash_remove_from_table (struct Lisp_Hash_Table *, Lisp_Object); extern struct hash_table_test const hashtest_eq, hashtest_eql, hashtest_equal; extern void validate_subarray (Lisp_Object, Lisp_Object, Lisp_Object, @@ -4041,6 +4285,7 @@ extern Lisp_Object concat3 (Lisp_Object, Lisp_Object, Lisp_Object); extern bool equal_no_quit (Lisp_Object, Lisp_Object); extern Lisp_Object nconc2 (Lisp_Object, Lisp_Object); extern Lisp_Object assq_no_quit (Lisp_Object, Lisp_Object); +extern Lisp_Object assq_no_signal (Lisp_Object, Lisp_Object); extern Lisp_Object assoc_no_quit (Lisp_Object, Lisp_Object); extern void clear_string_char_byte_cache (void); extern ptrdiff_t string_char_to_byte (Lisp_Object, ptrdiff_t); @@ -4052,9 +4297,11 @@ extern Lisp_Object plist_put (Lisp_Object plist, Lisp_Object prop, Lisp_Object val); extern Lisp_Object plist_member (Lisp_Object plist, Lisp_Object prop); extern void syms_of_fns (void); +extern void mark_fns (void); /* Defined in sort.c */ -extern void tim_sort (Lisp_Object, Lisp_Object *, const ptrdiff_t); +extern void tim_sort (Lisp_Object, Lisp_Object, Lisp_Object *, const ptrdiff_t, + bool); /* Defined in floatfns.c. */ verify (FLT_RADIX == 2 || FLT_RADIX == 16); @@ -4117,6 +4364,7 @@ extern void del_range_byte (ptrdiff_t, ptrdiff_t); extern void del_range_both (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); extern Lisp_Object del_range_2 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, bool); +extern int safe_del_range (ptrdiff_t, ptrdiff_t); extern void modify_text (ptrdiff_t, ptrdiff_t); extern void prepare_to_modify_buffer (ptrdiff_t, ptrdiff_t, ptrdiff_t *); extern void prepare_to_modify_buffer_1 (ptrdiff_t, ptrdiff_t, ptrdiff_t *); @@ -4432,6 +4680,9 @@ extern void syms_of_alloc (void); extern struct buffer *allocate_buffer (void) ATTRIBUTE_RETURNS_NONNULL; extern int valid_lisp_object_p (Lisp_Object); +void *hash_table_alloc_bytes (ptrdiff_t nbytes) ATTRIBUTE_MALLOC_SIZE ((1)); +void hash_table_free_bytes (void *p, ptrdiff_t nbytes); + /* Defined in gmalloc.c. */ #if !defined DOUG_LEA_MALLOC && !defined HYBRID_MALLOC && !defined SYSTEM_MALLOC extern size_t __malloc_extra_blocks; @@ -4493,7 +4744,6 @@ extern ptrdiff_t evxprintf (char **, ptrdiff_t *, char *, ptrdiff_t, ATTRIBUTE_FORMAT_PRINTF (5, 0); /* Defined in lread.c. */ -extern Lisp_Object check_obarray (Lisp_Object); extern Lisp_Object intern_1 (const char *, ptrdiff_t); extern Lisp_Object intern_c_string_1 (const char *, ptrdiff_t); extern Lisp_Object intern_driver (Lisp_Object, Lisp_Object, Lisp_Object); @@ -4509,7 +4759,8 @@ extern bool suffix_p (Lisp_Object, const char *); extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object, Lisp_Object); extern int openp (Lisp_Object, Lisp_Object, Lisp_Object, - Lisp_Object *, Lisp_Object, bool, bool); + Lisp_Object *, Lisp_Object, bool, bool, + void **); enum { S2N_IGNORE_TRAILING = 1 }; extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *); extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object), @@ -4538,7 +4789,6 @@ extern Lisp_Object Vrun_hooks; extern Lisp_Object Vsignaling_function; extern Lisp_Object inhibit_lisp_code; extern bool signal_quit_p (Lisp_Object); -extern bool backtrace_yet; /* To run a normal hook, use the appropriate function from the list below. The calling convention: @@ -4579,6 +4829,8 @@ extern Lisp_Object internal_condition_case_n extern Lisp_Object internal_catch_all (Lisp_Object (*) (void *), void *, Lisp_Object (*) (enum nonlocal_exit, Lisp_Object)); extern struct handler *push_handler (Lisp_Object, enum handlertype) ATTRIBUTE_RETURNS_NONNULL; +extern void pop_handler (void); +extern void push_handler_bind (Lisp_Object, Lisp_Object, int); extern struct handler *push_handler_nosignal (Lisp_Object, enum handlertype); extern void specbind (Lisp_Object, Lisp_Object); extern void record_unwind_protect (void (*) (Lisp_Object), Lisp_Object); @@ -4608,14 +4860,15 @@ extern Lisp_Object load_with_autoload_queue Lisp_Object nosuffix, Lisp_Object must_suffix); extern Lisp_Object call_debugger (Lisp_Object arg); extern void init_eval_once (void); -extern Lisp_Object safe_call (ptrdiff_t, Lisp_Object, ...); -extern Lisp_Object safe_call1 (Lisp_Object, Lisp_Object); -extern Lisp_Object safe_call2 (Lisp_Object, Lisp_Object, Lisp_Object); +extern Lisp_Object safe_funcall (ptrdiff_t, Lisp_Object*); +#define safe_calln(...) \ + CALLMANY (safe_funcall, ((Lisp_Object []) {__VA_ARGS__})) + extern void init_eval (void); extern void syms_of_eval (void); extern void prog_ignore (Lisp_Object); extern void mark_specpdl (union specbinding *first, union specbinding *ptr); -extern void get_backtrace (Lisp_Object array); +extern void get_backtrace (Lisp_Object *array, ptrdiff_t size); Lisp_Object backtrace_top_function (void); extern bool let_shadows_buffer_binding_p (struct Lisp_Symbol *symbol); void do_debug_on_call (Lisp_Object code, specpdl_ref count); @@ -4696,7 +4949,7 @@ extern void syms_of_editfns (void); /* Defined in buffer.c. */ extern bool mouse_face_overlay_overlaps (Lisp_Object); -extern Lisp_Object disable_line_numbers_overlay_at_eob (void); +extern bool disable_line_numbers_overlay_at_eob (void); extern AVOID nsberror (Lisp_Object); extern void adjust_overlays_for_insert (ptrdiff_t, ptrdiff_t, bool); extern void adjust_overlays_for_delete (ptrdiff_t, ptrdiff_t); @@ -4705,7 +4958,6 @@ extern void report_overlay_modification (Lisp_Object, Lisp_Object, bool, Lisp_Object, Lisp_Object, Lisp_Object); extern bool overlay_touches_p (ptrdiff_t); extern Lisp_Object other_buffer_safely (Lisp_Object); -extern Lisp_Object get_truename_buffer (Lisp_Object); extern void init_buffer_once (void); extern void init_buffer (void); extern void syms_of_buffer (void); @@ -4728,6 +4980,7 @@ extern void syms_of_marker (void); /* Defined in fileio.c. */ +extern Lisp_Object file_name_directory (Lisp_Object); extern char *splice_dir_file (char *, char const *, char const *) ATTRIBUTE_RETURNS_NONNULL; extern bool file_name_absolute_p (const char *); @@ -4806,7 +5059,7 @@ extern ptrdiff_t find_before_next_newline (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t *); extern EMACS_INT search_buffer (Lisp_Object, ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, EMACS_INT, - int, Lisp_Object, Lisp_Object, bool); + bool, Lisp_Object, Lisp_Object, bool); extern void syms_of_search (void); extern void clear_regexp_cache (void); @@ -4872,6 +5125,7 @@ extern void keys_of_keyboard (void); /* Defined in indent.c. */ extern ptrdiff_t current_column (void); +extern void line_number_display_width (struct window *, int *, int *); extern void invalidate_current_column (void); extern bool indented_beyond_p (ptrdiff_t, ptrdiff_t, EMACS_INT); extern void syms_of_indent (void); @@ -4923,6 +5177,7 @@ extern bool build_details; /* 0 not a daemon, 1 foreground daemon, 2 background daemon. */ extern int daemon_type; #define IS_DAEMON (daemon_type != 0) +/* Non-zero means daemon-initialized has not yet been called. */ #define DAEMON_RUNNING (daemon_type >= 0) #else /* WINDOWSNT */ extern void *w32_daemon_event; @@ -5067,11 +5322,34 @@ extern void init_random (void); extern void emacs_backtrace (int); extern AVOID emacs_abort (void) NO_INLINE; extern int emacs_fstatat (int, char const *, void *, int); +#ifdef HAVE_SYS_STAT_H +extern int sys_fstat (int, struct stat *); +#endif +extern int sys_faccessat (int, const char *, int, int); +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) extern int emacs_openat (int, char const *, int, int); +#endif extern int emacs_open (const char *, int, int); extern int emacs_open_noquit (const char *, int, int); extern int emacs_pipe (int[2]); extern int emacs_close (int); +#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) +# define emacs_fclose fclose +#else +extern int emacs_fclose (FILE *); +#endif +extern FILE *emacs_fdopen (int, const char *) + ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC (emacs_fclose, 1); +extern FILE *emacs_fopen (char const *, char const *) + ATTRIBUTE_MALLOC ATTRIBUTE_DEALLOC (emacs_fclose, 1); +extern int emacs_unlink (const char *); +extern int emacs_symlink (const char *, const char *); +extern int emacs_rmdir (const char *); +extern int emacs_mkdir (const char *, mode_t); +extern int emacs_renameat_noreplace (int, const char *, int, + const char *); +extern int emacs_rename (const char *, const char *); +extern int emacs_fchmodat (int, const char *, mode_t, int); extern ptrdiff_t emacs_read (int, void *, ptrdiff_t); extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t); extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t); @@ -5105,7 +5383,9 @@ extern Lisp_Object directory_files_internal (Lisp_Object, Lisp_Object, bool, Lisp_Object, Lisp_Object); /* Defined in term.c. */ +#ifndef HAVE_ANDROID extern int *char_ins_del_vector; +#endif extern void syms_of_term (void); extern AVOID fatal (const char *msgid, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2); @@ -5206,6 +5486,7 @@ void syms_of_dbusbind (void); extern bool profiler_memory_running; extern void malloc_probe (size_t); extern void syms_of_profiler (void); +extern void mark_profiler (void); #ifdef DOS_NT @@ -5213,6 +5494,17 @@ extern void syms_of_profiler (void); extern char *emacs_root_dir (void); #endif /* DOS_NT */ +#ifdef HAVE_TEXT_CONVERSION +/* Defined in textconv.c. */ +extern void reset_frame_state (struct frame *); +extern void report_selected_window_change (struct frame *); +extern void report_point_change (struct frame *, struct window *, + struct buffer *); +extern void disable_text_conversion (void); +extern void resume_text_conversion (void); +extern void syms_of_textconv (void); +#endif + #ifdef HAVE_NATIVE_COMP INLINE bool SUBR_NATIVE_COMPILEDP (Lisp_Object a) @@ -5402,14 +5694,22 @@ safe_free_unbind_to (specpdl_ref count, specpdl_ref sa_count, Lisp_Object val) return unbind_to (count, val); } +/* Work around GCC bug 109577 + https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109577 + which causes GCC to mistakenly complain about the + memory allocation in SAFE_ALLOCA_LISP_EXTRA. */ +#if GNUC_PREREQ (13, 0, 0) && !GNUC_PREREQ (14, 0, 0) +# pragma GCC diagnostic ignored "-Wanalyzer-allocation-size" +#endif + /* Set BUF to point to an allocated array of NELT Lisp_Objects, immediately followed by EXTRA spare bytes. */ #define SAFE_ALLOCA_LISP_EXTRA(buf, nelt, extra) \ do { \ ptrdiff_t alloca_nbytes; \ - if (INT_MULTIPLY_WRAPV (nelt, word_size, &alloca_nbytes) \ - || INT_ADD_WRAPV (alloca_nbytes, extra, &alloca_nbytes) \ + if (ckd_mul (&alloca_nbytes, nelt, word_size) \ + || ckd_add (&alloca_nbytes, alloca_nbytes, extra) \ || SIZE_MAX < alloca_nbytes) \ memory_full (SIZE_MAX); \ else if (alloca_nbytes <= sa_avail) \ |