<2023-11-06 Mon>
emacs
源码分析(三)这次来分析TAG_PTR
宏定义(将指针变成Lisp_Object
),首先看LISP_INITIALLY
在gdb
中展开的样子:
(gdb) macro exp LISP_INITIALLY(w)
expands to: (w)
从上面输出可以看出LISP_INITIALLY
的相关定义走的是这个分支:
/* A Lisp_Object is a tagged pointer or integer. Ordinarily it is a
Lisp_Word. However, if CHECK_LISP_OBJECT_TYPE, it is a wrapper
around Lisp_Word, to help catch thinkos like 'Lisp_Object x = 0;'.
LISP_INITIALLY (W) initializes a Lisp object with a tagged value
that is a Lisp_Word W. It can be used in a static initializer. */
typedef Lisp_Word Lisp_Object;
# define LISP_INITIALLY(w) (w)
enum CHECK_LISP_OBJECT_TYPE { CHECK_LISP_OBJECT_TYPE = false };
再看关于LISP_WORD_TAG(tag)
的gdb
相关输出:
(gdb) p pI
$23 = "l"
(gdb) p (int)EMACS_INT_WIDTH
$24 = 64
(gdb) p (int)VALBITS
$25 = 61
(gdb) p EMACS_INT_MAX
$26 = 9223372036854775807
(gdb) p VAL_MAX
$27 = 2305843009213693951
(gdb) macro exp LISP_WORD_TAG(tag)
expands to: ((Lisp_Word_tag) (tag) << (((0x7fffffffffffffffL >> (3 - 1)) / 2 < (0x7fffffffffffffffL)) ? 0 : VALBITS))
对应代码:
enum Lisp_Bits
{
/* Number of bits in a Lisp_Object value, not counting the tag. */
VALBITS = EMACS_INT_WIDTH - GCTYPEBITS,
/* Number of bits in a fixnum value, not counting the tag. */
FIXNUM_BITS = VALBITS + 1
};
/* Number of bits in a Lisp_Object tag. */
DEFINE_GDB_SYMBOL_BEGIN (int, GCTYPEBITS)
#define GCTYPEBITS 3
DEFINE_GDB_SYMBOL_END (GCTYPEBITS)
# elif INTPTR_MAX <= LONG_MAX && !defined WIDE_EMACS_INT
typedef long int EMACS_INT;
typedef unsigned long EMACS_UINT;
enum { EMACS_INT_WIDTH = LONG_WIDTH, EMACS_UINT_WIDTH = ULONG_WIDTH };
# define EMACS_INT_MAX LONG_MAX
# define pI "l"
/* The maximum value that can be stored in a EMACS_INT, assuming all
bits other than the type bits contribute to a nonnegative signed value.
This can be used in #if, e.g., '#if USE_LSB_TAG' below expands to an
expression involving VAL_MAX. */
#define VAL_MAX (EMACS_INT_MAX >> (GCTYPEBITS - 1))
/* Whether the least-significant bits of an EMACS_INT contain the tag.
On hosts where pointers-as-ints do not exceed VAL_MAX / 2, USE_LSB_TAG is:
a. unnecessary, because the top bits of an EMACS_INT are unused, and
b. slower, because it typically requires extra masking.
So, USE_LSB_TAG is true only on hosts where it might be useful. */
DEFINE_GDB_SYMBOL_BEGIN (bool, USE_LSB_TAG)
#define USE_LSB_TAG (VAL_MAX / 2 < INTPTR_MAX)
DEFINE_GDB_SYMBOL_END (USE_LSB_TAG)
看上面的VAL_MAX
定义,为什么它的值右移时要(GCTYPEBITS - 1)
这样减去一呢?我的理解是:
VALBITS
值是61
,译为val
值的bits
有效比特位有61
位。EMACS_INT_MAX
的值是9223372036854775807
,这是2^63-1
,虽然EMACS_INT_WIDTH
的值是64
,因为这是一个有符号类型,最高有效位是符号位,所以去除符号位后的val
值的有效位是63
位。GCTYPEBITS
不减去1
的话,右移3
位,只剩下有效的位数是60
位,即val
的bits
为60
,与VALBITS
矛盾了。再看:
(gdb) p LISP_WORDS_ARE_POINTERS
$28 = 1
/* 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. */
#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;
最后总体理解:
/* A integer value tagged with TAG, and otherwise all zero. */
#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)))
Lisp_Object
对象,它由ptr
和tag
组成。LISP_WORD_TAG(tag)
中,这里tag
表示tag
时直接返回(左移0
位);tag
表示含有tag
的整数时,左移VALBITS
位,获得真正的tag
值,然后返回。USE_LSB_TAG
表示传入的参数使用了tag
还是没有使用tag
(tag
或者没有使用tag