emacs 源码分析(三)

发布时间:2024年01月10日

<2023-11-06 Mon>

emacs源码分析(三)

这次来分析TAG_PTR宏定义(将指针变成Lisp_Object),首先看LISP_INITIALLYgdb中展开的样子:

(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)这样减去一呢?我的理解是:

  1. 记住VALBITS值是61,译为val值的bits有效比特位有61位。
  2. 再看EMACS_INT_MAX的值是9223372036854775807,这是2^63-1,虽然EMACS_INT_WIDTH的值是64,因为这是一个有符号类型,最高有效位是符号位,所以去除符号位后的val值的有效位是63位。
  3. 如果GCTYPEBITS不减去1的话,右移3位,只剩下有效的位数是60位,即valbits60,与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)))
  1. 最终生成一个Lisp_Object对象,它由ptrtag组成。
  2. 上面宏LISP_WORD_TAG(tag)中,这里tag表示tag时直接返回(左移0位);tag表示含有tag的整数时,左移VALBITS位,获得真正的tag值,然后返回。
  3. 所以USE_LSB_TAG表示传入的参数使用了tag还是没有使用tag而不是我之前理解的编译后就固定下来了,所有指针都使用了tag或者没有使用tag)。
文章来源:https://blog.csdn.net/ftuc5dn/article/details/135448428
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。