Turn VECTOR_FREE_LIST_FLAG into PVEC_FREE.

* src/lisp.h (enum pvec_type): Use fewer bits.
(PSEUDOVECTOR_SIZE_BITS): New constant.
(PSEUDOVECTOR_SIZE_MASK, PVEC_TYPE_MASK): Use it.
(XSETPVECTYPESIZE, XSETTYPED_PSEUDOVECTOR, DEFUN): Adapt code to
change in pvec_type.
(PSEUDOVECTOR_TYPEP): New macro.
(TYPED_PSEUDOVECTORP): Use it.
* src/fns.c (internal_equal): Adapt code to extract pvectype.
* src/emacs.c (gdb_pvec_type): Update type.
* src/alloc.c (PSEUDOVECTOR_NBYTES): New macro.
(VECTOR_FREE_LIST_SIZE_MASK): Remove (=> PSEUDOVECTOR_SIZE_MASK).
(VECTOR_FREE_LIST_FLAG): Remove (=> PVEC_FREE).
(SETUP_ON_FREE_LIST): Use XSETPVECTYPESIZE.
(sweep_vectors): Use it.  Use local var `total_bytes' instead of
abusing vector->header.next.nbytes.
(live_vector_p): Use PVEC_TYPE.
(mark_object): Adapt code to extract pvectype.  Use switch.
This commit is contained in:
Stefan Monnier 2012-07-04 10:38:02 -04:00
parent 1a9746debd
commit ee28be33a5
5 changed files with 188 additions and 130 deletions

View file

@ -1,3 +1,24 @@
2012-07-04 Stefan Monnier <monnier@iro.umontreal.ca>
Turn VECTOR_FREE_LIST_FLAG into PVEC_FREE.
* lisp.h (enum pvec_type): Use fewer bits.
(PSEUDOVECTOR_SIZE_BITS): New constant.
(PSEUDOVECTOR_SIZE_MASK, PVEC_TYPE_MASK): Use it.
(XSETPVECTYPESIZE, XSETTYPED_PSEUDOVECTOR, DEFUN): Adapt code to
change in pvec_type.
(PSEUDOVECTOR_TYPEP): New macro.
(TYPED_PSEUDOVECTORP): Use it.
* fns.c (internal_equal): Adapt code to extract pvectype.
* emacs.c (gdb_pvec_type): Update type.
* alloc.c (PSEUDOVECTOR_NBYTES): New macro.
(VECTOR_FREE_LIST_SIZE_MASK): Remove (=> PSEUDOVECTOR_SIZE_MASK).
(VECTOR_FREE_LIST_FLAG): Remove (=> PVEC_FREE).
(SETUP_ON_FREE_LIST): Use XSETPVECTYPESIZE.
(sweep_vectors): Use it. Use local var `total_bytes' instead of
abusing vector->header.next.nbytes.
(live_vector_p): Use PVEC_TYPE.
(mark_object): Adapt code to extract pvectype. Use switch.
2012-07-04 Paul Eggert <eggert@cs.ucla.edu>
* doprnt.c (doprnt): Don't assume string length fits in 'int'.

View file

@ -1529,7 +1529,7 @@ make_interval (void)
}
/* Mark Lisp objects in interval I. */
/* Mark Lisp objects in interval I. */
static void
mark_interval (register INTERVAL i, Lisp_Object dummy)
@ -1836,7 +1836,7 @@ check_sblock (struct sblock *b)
ptrdiff_t nbytes;
/* Check that the string size recorded in the string is the
same as the one recorded in the sdata structure. */
same as the one recorded in the sdata structure. */
if (from->string)
CHECK_STRING_BYTES (from->string);
@ -2869,12 +2869,6 @@ DEFUN ("make-list", Fmake_list, Smake_list, 2, 2, 0,
#define VECTOR_BLOCK_SIZE 4096
/* This special value is used to calculate vector size when the vector is
on a free list. It should be VECTOR_BLOCK_SIZE rounded up to nearest
power of two, minus one. */
#define VECTOR_FREE_LIST_SIZE_MASK 4095
/* Handy constants for vectorlike objects. */
enum
{
@ -2889,8 +2883,7 @@ verify ((roundup_size & (roundup_size - 1)) == 0);
/* Verify assumptions described above. */
verify ((VECTOR_BLOCK_SIZE % roundup_size) == 0);
verify ((VECTOR_FREE_LIST_SIZE_MASK + 1) >= VECTOR_BLOCK_SIZE);
verify ((VECTOR_FREE_LIST_SIZE_MASK & (VECTOR_FREE_LIST_SIZE_MASK + 1)) == 0);
verify (VECTOR_BLOCK_SIZE <= (1 << PSEUDOVECTOR_SIZE_BITS));
/* Round up X to nearest mult-of-ROUNDUP_SIZE. */
@ -2915,12 +2908,6 @@ verify ((VECTOR_FREE_LIST_SIZE_MASK & (VECTOR_FREE_LIST_SIZE_MASK + 1)) == 0);
#define VECTOR_MAX_FREE_LIST_INDEX \
((VECTOR_BLOCK_BYTES - VBLOCK_BYTES_MIN) / roundup_size + 1)
/* When the vector is on a free list, vectorlike_header.SIZE is set to
this special value ORed with vector's memory footprint size. */
#define VECTOR_FREE_LIST_FLAG (~(ARRAY_MARK_FLAG | PSEUDOVECTOR_FLAG \
| VECTOR_FREE_LIST_SIZE_MASK))
/* Common shortcut to advance vector pointer over a block data. */
#define ADVANCE(v, nbytes) ((struct Lisp_Vector *) ((char *) (v) + (nbytes)))
@ -2933,7 +2920,7 @@ verify ((VECTOR_FREE_LIST_SIZE_MASK & (VECTOR_FREE_LIST_SIZE_MASK + 1)) == 0);
#define SETUP_ON_FREE_LIST(v, nbytes, index) \
do { \
(v)->header.size = VECTOR_FREE_LIST_FLAG | (nbytes); \
XSETPVECTYPESIZE (v, PVEC_FREE, nbytes); \
eassert ((nbytes) % roundup_size == 0); \
(index) = VINDEX (nbytes); \
eassert ((index) < VECTOR_MAX_FREE_LIST_INDEX); \
@ -3065,6 +3052,16 @@ allocate_vector_from_block (size_t nbytes)
((char *) (vector) <= (block)->data \
+ VECTOR_BLOCK_BYTES - VBLOCK_BYTES_MIN)
/* Number of bytes used by vector-block-allocated object. This is the only
place where we actually use the `nbytes' field of the vector-header.
I.e. we could get rid of the `nbytes' field by computing it based on the
vector-type. */
#define PSEUDOVECTOR_NBYTES(vector) \
(PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FREE) \
? vector->header.size & PSEUDOVECTOR_SIZE_MASK \
: vector->header.next.nbytes);
/* Reclaim space used by unmarked vectors. */
static void
@ -3093,14 +3090,10 @@ sweep_vectors (void)
}
else
{
ptrdiff_t nbytes;
ptrdiff_t nbytes = PSEUDOVECTOR_NBYTES (vector);
ptrdiff_t total_bytes = nbytes;
if ((vector->header.size & VECTOR_FREE_LIST_FLAG)
== VECTOR_FREE_LIST_FLAG)
vector->header.next.nbytes =
vector->header.size & VECTOR_FREE_LIST_SIZE_MASK;
next = ADVANCE (vector, vector->header.next.nbytes);
next = ADVANCE (vector, nbytes);
/* While NEXT is not marked, try to coalesce with VECTOR,
thus making VECTOR of the largest possible size. */
@ -3109,16 +3102,12 @@ sweep_vectors (void)
{
if (VECTOR_MARKED_P (next))
break;
if ((next->header.size & VECTOR_FREE_LIST_FLAG)
== VECTOR_FREE_LIST_FLAG)
nbytes = next->header.size & VECTOR_FREE_LIST_SIZE_MASK;
else
nbytes = next->header.next.nbytes;
vector->header.next.nbytes += nbytes;
nbytes = PSEUDOVECTOR_NBYTES (next);
total_bytes += nbytes;
next = ADVANCE (next, nbytes);
}
eassert (vector->header.next.nbytes % roundup_size == 0);
eassert (total_bytes % roundup_size == 0);
if (vector == (struct Lisp_Vector *) block->data
&& !VECTOR_IN_BLOCK (next, block))
@ -3126,7 +3115,10 @@ sweep_vectors (void)
space was coalesced into the only free vector. */
free_this_block = 1;
else
SETUP_ON_FREE_LIST (vector, vector->header.next.nbytes, nbytes);
{
int tmp;
SETUP_ON_FREE_LIST (vector, total_bytes, tmp);
}
}
}
@ -4342,10 +4334,9 @@ live_vector_p (struct mem_node *m, void *p)
while (VECTOR_IN_BLOCK (vector, block)
&& vector <= (struct Lisp_Vector *) p)
{
if ((vector->header.size & VECTOR_FREE_LIST_FLAG)
== VECTOR_FREE_LIST_FLAG)
if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_FREE))
vector = ADVANCE (vector, (vector->header.size
& VECTOR_FREE_LIST_SIZE_MASK));
& PSEUDOVECTOR_SIZE_MASK));
else if (vector == p)
return 1;
else
@ -5913,15 +5904,17 @@ mark_object (Lisp_Object arg)
#endif /* GC_CHECK_MARKED_OBJECTS */
if (ptr->header.size & PSEUDOVECTOR_FLAG)
pvectype = ptr->header.size & PVEC_TYPE_MASK;
pvectype = ((ptr->header.size & PVEC_TYPE_MASK)
>> PSEUDOVECTOR_SIZE_BITS);
else
pvectype = 0;
if (pvectype != PVEC_SUBR && pvectype != PVEC_BUFFER)
CHECK_LIVE (live_vector_p);
if (pvectype == PVEC_BUFFER)
switch (pvectype)
{
case PVEC_BUFFER:
#ifdef GC_CHECK_MARKED_OBJECTS
if (po != &buffer_defaults && po != &buffer_local_symbols)
{
@ -5933,67 +5926,82 @@ mark_object (Lisp_Object arg)
}
#endif /* GC_CHECK_MARKED_OBJECTS */
mark_buffer ((struct buffer *) ptr);
}
break;
else if (pvectype == PVEC_COMPILED)
/* We could treat this just like a vector, but it is better
to save the COMPILED_CONSTANTS element for last and avoid
recursion there. */
{
int size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK;
int i;
case PVEC_COMPILED:
{ /* We could treat this just like a vector, but it is better
to save the COMPILED_CONSTANTS element for last and avoid
recursion there. */
int size = ptr->header.size & PSEUDOVECTOR_SIZE_MASK;
int i;
VECTOR_MARK (ptr);
for (i = 0; i < size; i++)
if (i != COMPILED_CONSTANTS)
mark_object (ptr->contents[i]);
if (size > COMPILED_CONSTANTS)
{
obj = ptr->contents[COMPILED_CONSTANTS];
goto loop;
}
}
break;
case PVEC_FRAME:
{
mark_vectorlike (ptr);
mark_face_cache (((struct frame *) ptr)->face_cache);
}
break;
case PVEC_WINDOW:
{
struct window *w = (struct window *) ptr;
mark_vectorlike (ptr);
/* Mark glyphs for leaf windows. Marking window
matrices is sufficient because frame matrices
use the same glyph memory. */
if (NILP (w->hchild) && NILP (w->vchild) && w->current_matrix)
{
mark_glyph_matrix (w->current_matrix);
mark_glyph_matrix (w->desired_matrix);
}
}
break;
case PVEC_HASH_TABLE:
{
struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *) ptr;
mark_vectorlike (ptr);
/* If hash table is not weak, mark all keys and values.
For weak tables, mark only the vector. */
if (NILP (h->weak))
mark_object (h->key_and_value);
else
VECTOR_MARK (XVECTOR (h->key_and_value));
}
break;
case PVEC_CHAR_TABLE:
mark_char_table (ptr);
break;
case PVEC_BOOL_VECTOR:
/* No Lisp_Objects to mark in a bool vector. */
VECTOR_MARK (ptr);
for (i = 0; i < size; i++)
if (i != COMPILED_CONSTANTS)
mark_object (ptr->contents[i]);
obj = ptr->contents[COMPILED_CONSTANTS];
goto loop;
}
break;
else if (pvectype == PVEC_FRAME)
{
case PVEC_SUBR:
break;
case PVEC_FREE:
abort ();
default:
mark_vectorlike (ptr);
mark_face_cache (((struct frame *) ptr)->face_cache);
}
else if (pvectype == PVEC_WINDOW)
{
struct window *w = (struct window *) ptr;
mark_vectorlike (ptr);
/* Mark glyphs for leaf windows. Marking window
matrices is sufficient because frame matrices
use the same glyph memory. */
if (NILP (w->hchild) && NILP (w->vchild) && w->current_matrix)
{
mark_glyph_matrix (w->current_matrix);
mark_glyph_matrix (w->desired_matrix);
}
}
else if (pvectype == PVEC_HASH_TABLE)
{
struct Lisp_Hash_Table *h = (struct Lisp_Hash_Table *) ptr;
mark_vectorlike (ptr);
/* If hash table is not weak, mark all keys and values.
For weak tables, mark only the vector. */
if (NILP (h->weak))
mark_object (h->key_and_value);
else
VECTOR_MARK (XVECTOR (h->key_and_value));
}
else if (pvectype == PVEC_CHAR_TABLE)
mark_char_table (ptr);
else if (pvectype == PVEC_BOOL_VECTOR)
/* No Lisp_Objects to mark in a bool vector. */
VECTOR_MARK (ptr);
else if (pvectype != PVEC_SUBR)
mark_vectorlike (ptr);
}
break;

View file

@ -119,7 +119,7 @@ ptrdiff_t PVEC_FLAG EXTERNALLY_VISIBLE = PSEUDOVECTOR_FLAG;
ptrdiff_t gdb_array_mark_flag EXTERNALLY_VISIBLE = ARRAY_MARK_FLAG;
/* GDB might say "No enum type named pvec_type" if we don't have at
least one symbol with that type, and then xbacktrace could fail. */
enum pvec_type gdb_pvec_type EXTERNALLY_VISIBLE = PVEC_TYPE_MASK;
ptrdiff_t gdb_pvec_type EXTERNALLY_VISIBLE = PVEC_TYPE_MASK;
/* Empty lisp strings. To avoid having to build any others. */
Lisp_Object empty_unibyte_string, empty_multibyte_string;

View file

@ -2095,8 +2095,9 @@ internal_equal (register Lisp_Object o1, register Lisp_Object o2, int depth, int
are sensible to compare, so eliminate the others now. */
if (size & PSEUDOVECTOR_FLAG)
{
if (!(size & (PVEC_COMPILED
| PVEC_CHAR_TABLE | PVEC_SUB_CHAR_TABLE | PVEC_FONT)))
if (!(size & ((PVEC_COMPILED | PVEC_CHAR_TABLE
| PVEC_SUB_CHAR_TABLE | PVEC_FONT)
<< PSEUDOVECTOR_SIZE_BITS)))
return 0;
size &= PSEUDOVECTOR_SIZE_MASK;
}

View file

@ -341,28 +341,26 @@ typedef EMACS_INT Lisp_Object;
It is not crucial, but there are plenty of bits here, so why not do it? */
enum pvec_type
{
PVEC_NORMAL_VECTOR = 0,
PVEC_PROCESS = 0x200,
PVEC_FRAME = 0x400,
PVEC_COMPILED = 0x800,
PVEC_WINDOW = 0x1000,
PVEC_WINDOW_CONFIGURATION = 0x2000,
PVEC_SUBR = 0x4000,
PVEC_CHAR_TABLE = 0x8000,
PVEC_BOOL_VECTOR = 0x10000,
PVEC_BUFFER = 0x20000,
PVEC_HASH_TABLE = 0x40000,
PVEC_TERMINAL = 0x80000,
PVEC_SUB_CHAR_TABLE = 0x100000,
PVEC_FONT = 0x200000,
PVEC_OTHER = 0x400000,
PVEC_TYPE_MASK = 0x7ffe00
#if 0 /* This is used to make the value of PSEUDOVECTOR_FLAG available to
GDB. It doesn't work on OS Alpha. Moved to a variable in
emacs.c. */
PVEC_FLAG = PSEUDOVECTOR_FLAG
#endif
PVEC_NORMAL_VECTOR = 0, /* Unused! */
PVEC_FREE,
PVEC_PROCESS,
PVEC_FRAME,
PVEC_WINDOW,
PVEC_BOOL_VECTOR,
PVEC_BUFFER,
PVEC_HASH_TABLE,
PVEC_TERMINAL,
PVEC_WINDOW_CONFIGURATION,
PVEC_SUBR,
PVEC_OTHER,
/* These last 4 are special because we OR them in fns.c:internal_equal,
so they have to use a disjoint bit pattern:
if (!(size & (PVEC_COMPILED | PVEC_CHAR_TABLE
| PVEC_SUB_CHAR_TABLE | PVEC_FONT))) */
PVEC_COMPILED = 0x10,
PVEC_CHAR_TABLE = 0x20,
PVEC_SUB_CHAR_TABLE = 0x30,
PVEC_FONT = 0x40
};
/* For convenience, we also store the number of elements in these bits.
@ -370,7 +368,9 @@ enum pvec_type
only the number of Lisp_Object fields (that need to be traced by the GC).
The distinction is used e.g. by Lisp_Process which places extra
non-Lisp_Object fields at the end of the structure. */
#define PSEUDOVECTOR_SIZE_MASK 0x1ff
#define PSEUDOVECTOR_SIZE_BITS 16
#define PSEUDOVECTOR_SIZE_MASK ((1 << PSEUDOVECTOR_SIZE_BITS) - 1)
#define PVEC_TYPE_MASK (0x0fff << PSEUDOVECTOR_SIZE_BITS)
/* Number of bits to put in each character in the internal representation
of bool vectors. This should not vary across implementations. */
@ -536,9 +536,11 @@ clip_to_bounds (ptrdiff_t lower, EMACS_INT num, ptrdiff_t upper)
#define XSETPVECTYPE(v, code) XSETTYPED_PVECTYPE (v, header.size, code)
#define XSETTYPED_PVECTYPE(v, size_member, code) \
((v)->size_member |= PSEUDOVECTOR_FLAG | (code))
((v)->size_member |= PSEUDOVECTOR_FLAG | ((code) << PSEUDOVECTOR_SIZE_BITS))
#define XSETPVECTYPESIZE(v, code, sizeval) \
((v)->header.size = PSEUDOVECTOR_FLAG | (code) | (sizeval))
((v)->header.size = (PSEUDOVECTOR_FLAG \
| ((code) << PSEUDOVECTOR_SIZE_BITS) \
| (sizeval)))
/* The cast to struct vectorlike_header * avoids aliasing issues. */
#define XSETPSEUDOVECTOR(a, b, code) \
@ -550,7 +552,7 @@ clip_to_bounds (ptrdiff_t lower, EMACS_INT num, ptrdiff_t upper)
#define XSETTYPED_PSEUDOVECTOR(a, b, size, code) \
(XSETVECTOR (a, b), \
eassert ((size & (PSEUDOVECTOR_FLAG | PVEC_TYPE_MASK)) \
== (PSEUDOVECTOR_FLAG | (code))))
== (PSEUDOVECTOR_FLAG | (code << PSEUDOVECTOR_SIZE_BITS))))
#define XSETWINDOW_CONFIGURATION(a, b) \
(XSETPSEUDOVECTOR (a, b, PVEC_WINDOW_CONFIGURATION))
@ -730,13 +732,13 @@ extern ptrdiff_t string_bytes (struct Lisp_String *);
/* Set text properties. */
#define STRING_SET_INTERVALS(STR, INT) (XSTRING (STR)->intervals = (INT))
/* In a string or vector, the sign bit of the `size' is the gc mark bit */
/* In a string or vector, the sign bit of the `size' is the gc mark bit. */
struct Lisp_String
{
ptrdiff_t size;
ptrdiff_t size_byte;
INTERVAL intervals; /* text properties in this string */
INTERVAL intervals; /* Text properties in this string. */
unsigned char *data;
};
@ -749,6 +751,20 @@ struct Lisp_String
<http://debbugs.gnu.org/cgi/bugreport.cgi?bug=8546>. */
struct vectorlike_header
{
/* This field 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
a "pvec type" tag held in PVEC_TYPE_MASK and a size held in the lowest
PSEUDOVECTOR_SIZE_BITS. That size normally indicates the number of
Lisp_Object slots at the beginning of the object that need to be
traced by the GC, tho some types use it slightly differently.
- E.g. if the pvec type is PVEC_FREE it means this is an unallocated
vector on a free-list and PSEUDOVECTOR_SIZE_BITS indicates its size
in bytes. */
ptrdiff_t size;
/* When the vector is allocated from a vector block, NBYTES is used
@ -759,8 +775,17 @@ struct vectorlike_header
pointer: this way, one can write P->next.vector instead of ((struct
Lisp_Vector *) P->next). */
union {
/* This is only needed for small vectors that are not free because the
`size' field only gives us the number of Lisp_Object slots, whereas we
need to know the total size, including non-Lisp_Object data.
FIXME: figure out a way to store this info elsewhere so we can
finally get rid of this extra word of overhead. */
ptrdiff_t nbytes;
struct buffer *buffer;
/* FIXME: This can be removed: For large vectors, this field could be
placed *before* the vector itself. And for small vectors on a free
list, this field could be stored in the vector's bytes, since the
empty vector is handled specially anyway. */
struct Lisp_Vector *vector;
} next;
};
@ -775,7 +800,7 @@ struct Lisp_Vector
of the shortest vector that would hold that struct. */
#define VECSIZE(type) ((sizeof (type) \
- offsetof (struct Lisp_Vector, contents[0]) \
+ sizeof (Lisp_Object) - 1) /* round up */ \
+ sizeof (Lisp_Object) - 1) /* Round up. */ \
/ sizeof (Lisp_Object))
/* Like VECSIZE, but used when the pseudo-vector has non-Lisp_Object fields
@ -1617,15 +1642,17 @@ typedef struct {
/* True if object X is a pseudovector whose code is CODE. The cast to struct
vectorlike_header * avoids aliasing issues. */
#define PSEUDOVECTORP(x, code) \
TYPED_PSEUDOVECTORP(x, vectorlike_header, code)
TYPED_PSEUDOVECTORP (x, vectorlike_header, code)
#define PSEUDOVECTOR_TYPEP(v, code) \
(((v)->size & (PSEUDOVECTOR_FLAG | PVEC_TYPE_MASK)) \
== (PSEUDOVECTOR_FLAG | ((code) << PSEUDOVECTOR_SIZE_BITS)))
/* True if object X, with internal type struct T *, is a pseudovector whose
code is CODE. */
#define TYPED_PSEUDOVECTORP(x, t, code) \
(VECTORLIKEP (x) \
&& (((((struct t *) XUNTAG (x, Lisp_Vectorlike))->size \
& (PSEUDOVECTOR_FLAG | (code)))) \
== (PSEUDOVECTOR_FLAG | (code))))
&& PSEUDOVECTOR_TYPEP ((struct t *) XUNTAG (x, Lisp_Vectorlike), code))
/* Test for specific pseudovector types. */
#define WINDOW_CONFIGURATIONP(x) PSEUDOVECTORP (x, PVEC_WINDOW_CONFIGURATION)
@ -1818,7 +1845,8 @@ typedef struct {
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \
Lisp_Object fnname DEFUN_ARGS_ ## maxargs ; \
static DECL_ALIGN (struct Lisp_Subr, sname) = \
{ PVEC_SUBR | (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)), \
{ (PVEC_SUBR << PSEUDOVECTOR_SIZE_BITS) \
| (sizeof (struct Lisp_Subr) / sizeof (EMACS_INT)), \
{ (Lisp_Object (__cdecl *)(void))fnname }, \
minargs, maxargs, lname, intspec, 0}; \
Lisp_Object fnname
@ -1826,7 +1854,7 @@ typedef struct {
#define DEFUN(lname, fnname, sname, minargs, maxargs, intspec, doc) \
Lisp_Object fnname DEFUN_ARGS_ ## maxargs ; \
static DECL_ALIGN (struct Lisp_Subr, sname) = \
{ PVEC_SUBR, \
{ PVEC_SUBR << PSEUDOVECTOR_SIZE_BITS, \
{ .a ## maxargs = fnname }, \
minargs, maxargs, lname, intspec, 0}; \
Lisp_Object fnname