Common Lisp的值类型及SBCL中的底层表示

来源:互联网 发布:剑三花哥脸型数据 编辑:程序博客网 时间:2024/05/29 06:46

动态类型 vs. 静态类型

众所周知Common Lisp是一种动态类型语言,

虽然编译器会在编译时检查类型, 但这种检查往往出于编译优化的目的,而不是为了类型安全的严格检查。

至于动态类型,喜欢的人喜欢它,赞扬它可以让代码简洁,开发高效, 厌恶的人厌恶它,批评它不够安全,无法协同工作构建复杂系统。

这也正是是动态类型语言的优美和邪恶的地方。


在Common Lisp里, 你可以通过declare/declaim的type/ftype子句在代码中声明值(对象)类型,也可以通过the来指定值(对象)类型(declare/declaim无非是the的语法糖),

但Common Lisp并不保证如果你在运行时给出一个非该类型的值(对象)会发生什么,行为是undefined。

另外,Common Lisp也不会作型别推倒, 函数的参数的类型无法自动的限定返回值类型。

尽管如此, 一般CL的最佳实践还是建议大家指明所使用的类型, 这样可能会得到更快的运行代码。


值(对象)的类型

很久前收藏的一幅Common Lisp的类型关系图, 看完后你会为它标准库里的类型个数吓倒。

为了让讨论简单起见, 暂时忽略中间CLOS的standard-object相关的部分(CLOS可以视为相对独立的部分),

CL标准中定义的基本类型包括了,

  • 字符character, 分为支持基本字符集的base-char(ascii)和扩展字符集的extended-char(unicode)
  • 函数function, 如果是编译型的,那就等于compiled-function
  • 路径pathname
  • 流stream和一大堆具体的功能的流
  • 数组array, 一维的vector和多维的simple-array
  • 序列sequence, 数组vector和列表list, list又有cons和nil
  • 字串string是包含character的vector
  • 符号symbol(类似于其他语言标识符的地位)
  • 数字number和各种细分的数字类型

其余还有众多的错误和警告的condition(类似于OOP中的exception的地位), 包package, 哈希表hash-table, reader的read-table等等


从这个类型关系里我们可以发现几个特点,

  • 有些子类型同时从属于两种父类型,即类型有交集的部分,特别是序列sequence,符号symbol和数组array。
  • 集和类型因为其包含的元素类型不同也有细分,如vector细分为string, bit-vector
  • 类型的分类并不是单一一个角度的, 如atom




SBCL中类型的底层表示

这里要讨论的是SBCL中关于上述的值类型在runtime里是以什么形式表示的。 (主要的参考资料是CMUCL desgin)

由于是动态类型, 类型是在运行期绑定在值(对象)上的, 因此, 所谓的类型的表示形式,其实就是各种值(对象)在runtime里的表示形式。

这里仅从32位 80x86系统的SBCL运行时的现状出发分析值及其类型的表示形式。


为了搞清楚底层表示,首先了解一下术语,

首先, SBCL中的值是以32位(8字节)为存储单元(最小存储单位)进行存储的, 32位的数据长度在SBCL中被称作word。

其次,一般值的第一个word的低3位,被用作表示该值所属的大类型的划分, 这3位被称作low-tag,

(仅仅靠low-tag的3位是无法区分所有值的类型的, 而且除去low-tag的word的其余28位也无法放下大部分类型的值。 )

需要占用一定连续的存储单元(在堆上的连续word)的值,占用的连续区域被称作data-block。

这样的值通常是由一个存储了指向data-block的指针的一个word和存储了实际值的data-block(至少两个word以上)构成,

data-block的第一个word的低8位一般用于表示该值所属的小类型, 这8位被称作wide-tag(其中的低3位仍然是low-tag)。


SBCL中关于类型的信息主要存放在low-tag和wide-tag里, 因此,通过掌握SBCL中预先定义的wide-tag和low-tag,

我们就能一窥关于值的类型的表示。 通过apropos, 我们可以看到wide-tag和low-tag主要定义在sb-vm的包中(early-objdef.lisp文件),

大致有以下这些:

low-tag

  • ODD-FIXNUM-LOWTAG : 100
  • EVEN-FIXNUM-LOWTAG : 000
  • FUN-POINTER-LOWTAG : 101
  • INSTANCE-POINTER-LOWTAG : 001
  • LIST-POINTER-LOWTAG : 011
  • OTHER-POINTER-LOWTAG : 111                 // not function, clos instance, or cons
  • OTHER-IMMEDIATE-0-LOWTAG : 010        // character
  • OTHER-IMMEDIATE-1-LOWTAG : 110

wide-tag

  • BIGNUM-WIDETAG : 00001010
  • RATIO-WIDETAG : 00001110
  • DOUBLE-FLOAT-WIDETAG : 00010110
  • SINGLE-FLOAT-WIDETAG : 00010010
  • COMPLEX-WIDETAG : 00011010
  • COMPLEX-DOUBLE-FLOAT-WIDETAG : 00100010
  • COMPLEX-SINGLE-FLOAT-WIDETAG : 00011110
  • CHARACTER-WIDETAG : 01000010
  • SYMBOL-HEADER-WIDETAG : 00111110
  • COMPLEX-ARRAY-WIDETAG : 11111010
  • COMPLEX-VECTOR-WIDETAG : 11110110
  • COMPLEX-VECTOR-NIL-WIDETAG : 11101110                           // empty string
  • COMPLEX-BASE-STRING-WIDETAG : 11101010                        // non-empty string
  • COMPLEX-CHARACTER-STRING-WIDETAG : 11100110
  • COMPLEX-BIT-VECTOR-WIDETAG : 11110010
  • SIMPLE-ARRAY-COMPLEX-DOUBLE-FLOAT-WIDETAG : 11001110
  • SIMPLE-ARRAY-COMPLEX-SINGLE-FLOAT-WIDETAG : 11001010
  • SIMPLE-ARRAY-DOUBLE-FLOAT-WIDETAG : 11000110
  • SIMPLE-ARRAY-SINGLE-FLOAT-WIDETAG : 11000010
  • SIMPLE-ARRAY-FIXNUM-WIDETAG : 10111010
  • SIMPLE-ARRAY-UNSIGNED-FIXNUM-WIDETAG : 10100110
  • SIMPLE-ARRAY-SIGNED-BYTE-16-WIDETAG : 10110110
  • SIMPLE-ARRAY-SIGNED-BYTE-32-WIDETAG : 10111110
  • SIMPLE-ARRAY-SIGNED-BYTE-8-WIDETAG : 10110010
  • SIMPLE-ARRAY-UNSIGNED-BYTE-15-WIDETAG : 10011110
  • SIMPLE-ARRAY-UNSIGNED-BYTE-16-WIDETAG : 10100010
  • SIMPLE-ARRAY-UNSIGNED-BYTE-2-WIDETAG : 10001110
  • SIMPLE-ARRAY-UNSIGNED-BYTE-31-WIDETAG : 10101010
  • SIMPLE-ARRAY-UNSIGNED-BYTE-32-WIDETAG : 10101110
  • SIMPLE-ARRAY-UNSIGNED-BYTE-4-WIDETAG : 10010010
  • SIMPLE-ARRAY-UNSIGNED-BYTE-7-WIDETAG : 10010110
  • SIMPLE-ARRAY-UNSIGNED-BYTE-8-WIDETAG : 10011010
  • SIMPLE-ARRAY-WIDETAG : 10001010
  • SIMPLE-ARRAY-NIL-WIDETAG : 11011010                      // empty string
  • SIMPLE-BASE-STRING-WIDETAG : 11011110                 // non-empty string
  • SIMPLE-CHARACTER-STRING-WIDETAG : 11100010
  • SIMPLE-BIT-VECTOR-WIDETAG : 11010010
  • SIMPLE-VECTOR-WIDETAG : 11010110
  • SAP-WIDETAG : 01000110                                                   // system area pointer (an abstraction of c pointer like object)
  • CODE-HEADER-WIDETAG : 00100110
  • RETURN-PC-HEADER-WIDETAG : 00110110
  • SIMPLE-FUN-HEADER-WIDETAG : 00101010
  • CLOSURE-HEADER-WIDETAG : 00101110
  • FUNCALLABLE-INSTANCE-HEADER-WIDETAG : 00110010
  • FDEFN-WIDETAG : 01010110
  • INSTANCE-HEADER-WIDETAG : 01010010
  • NO-TLS-VALUE-MARKER-WIDETAG : 01011010           // thread related
  • UNBOUND-MARKER-WIDETAG : 01001010                   // clos slot-value unbound related
  • VALUE-CELL-HEADER-WIDETAG : 00111010
  • WEAK-POINTER-WIDETAG : 01001110

另外,在sb-kernel包(kernel.lisp)中有lowtag-of, widetag-of或%other-pointer-widetag可以获得参数值的lowtag和widetag。

(未完待续。。。)


0 0