理解SBCL进出汇编锅炉板代码

13

背景

在Windows上使用64位Steel Bank Common Lisp实现一个简单的身份函数时:

(defun a (x)
   (declare (fixnum x)) 
   (declare (optimize (speed 3) (safety 0))) 
  (the fixnum x))

我的发现,拆卸的步骤如下:

* (disassemble 'a)

; disassembly for A
; Size: 13 bytes
; 02D7DFA6:       84042500000F20   TEST AL, [#x200F0000]      ; safepoint
                                                              ; no-arg-parsing entry point
;       AD:       488BE5           MOV RSP, RBP
;       B0:       F8               CLC
;       B1:       5D               POP RBP
;       B2:       C3               RET

我理解这个句子的意思:

mov rsp, rbp
pop rbp
ret  

执行函数的标准返回操作,但我不明白为什么会有这些代码行:

TEST AL, [#x200F0000]  // My understanding is that this sets flags based on bitwise and of AL and contents of memory 0x200F0000

以及

CLC // My understanding is that this clears the carry flag.

问题

  1. 为什么SBCL会生成测试指令,却从未使用过标志位?
  2. 为什么SBCL在从函数返回之前清除进位标志?

调用约定可能是什么?这是启用了优化吗? - Niklas B.
我的理解是,我的优化声明说明它要编译速度而不是安全性。(没有这些,代码会更长) - Peter de Rivaz
1
@AndrewMyers:确认。在64位Linux上,没有测试指令。可能与Windows ABI有关。 - Linuxios
1
在SBCL使用的至少一个平台上,状态标志用于表示它是否为单值返回。 - Philipp Matthias Schäfer
1
由于我只能编辑五分钟的评论,这里附上相关代码(包含注释)链接: 编译器代码 - Philipp Matthias Schäfer
显示剩余4条评论
2个回答

8
正如反汇编器所提示的那样,TEST指令是一个安全点。它用于为垃圾回收器同步线程。安全点插入在编译器知道线程处于安全状态以进行垃圾回收的地方。
安全点的形式在compiler/x86-64/macros.lisp中定义:
#!+sb-safepoint
(defun emit-safepoint ()
  (inst test al-tn (make-ea :byte :disp sb!vm::gc-safepoint-page-addr)))

当然了,您关于操作结果未被使用是正确的。在这种情况下,SBCL对操作的副作用感兴趣。具体来说,如果包含该地址的页面受到保护,该指令将生成一个页错误。如果该页面可访问,则该指令只会浪费非常少的时间。我应该指出,这可能比简单检查全局变量要快得多。
在Windows上,runtime/win32-os.c 中的C函数 map_gc_pageunmap_gc_page 用于映射和取消映射页面:
void map_gc_page()
{
    DWORD oldProt;
    AVER(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, sizeof(lispobj),
                        PAGE_READWRITE, &oldProt));
}

void unmap_gc_page()
{
    DWORD oldProt;
    AVER(VirtualProtect((void*) GC_SAFEPOINT_PAGE_ADDR, sizeof(lispobj),
                        PAGE_NOACCESS, &oldProt));
}

很遗憾,我还没有找到页面故障处理程序,但总体思路似乎是需要集合时,将调用unmap_gc_page。每个线程将继续运行,直到达到其中一个安全点,然后发生页面故障。假设页面故障处理程序将暂停该线程,然后当所有线程都被暂停时,垃圾收集运行,然后再次调用map_gc_page并允许线程恢复。
学分文件向Anton Kovalenko致敬介绍了这种机制。
在Linux和Mac OS X上,默认使用不同的同步机制,这就是为什么默认构建这些平台上不会生成指令的原因。(我不确定PowerPC端口是否默认使用安全点,但显然它们不使用x86指令)。
另一方面,我对CLC指令一无所知。

哇!太有趣了!使用乱序处理器,我想知道函数是否存在执行不安全操作的风险,但是通过添加CLC指令,它会强制进行安全点检查以确保已完成? - Peter de Rivaz
@PeterdeRivaz,CLC指令出现在非安全点构建中,因此我怀疑它与安全点无关。 - Samuel Edwin Ward

5
我对TEST AL,[#x200F0000]一无所知,但我认为CLC是用于返回单个值的函数。 SBCL内部手册“未知值返回”表明,如果函数返回多个值,则将设置进位标志,如果函数返回一个值,则清除进位标志。
我正在运行OpenBSD和x86-64的SBCL 1.1.14。如果我反汇编返回单个值的函数和返回多个值的函数,我可以看到CLCSEC
CL-USER> (disassemble (lambda () 100))
; disassembly for (LAMBDA ())
; Size: 16 bytes
; 04B36F64:       BAC8000000       MOV EDX, 200               ; no-arg-parsing entry point
;       69:       488BE5           MOV RSP, RBP
;       6C:       F8               CLC
;       6D:       5D               POP RBP
;       6E:       C3               RET
;       6F:       CC0A             BREAK 10                   ; error trap
;       71:       02               BYTE #X02
;       72:       19               BYTE #X19                  ; INVALID-ARG-COUNT-ERROR
;       73:       9A               BYTE #X9A                  ; RCX
NIL

这个指令有CLC(清除进位)因为它返回一个值。

CL-USER> (disassemble (lambda () (values 100 200)))
; disassembly for (LAMBDA ())
; Size: 35 bytes
; 04B82BD4:       BAC8000000       MOV EDX, 200               ; no-arg-parsing entry point
;       D9:       BF90010000       MOV EDI, 400
;       DE:       488D5D10         LEA RBX, [RBP+16]
;       E2:       B904000000       MOV ECX, 4
;       E7:       BE17001020       MOV ESI, 537919511
;       EC:       F9               STC
;       ED:       488BE5           MOV RSP, RBP
;       F0:       5D               POP RBP
;       F1:       C3               RET
;       F2:       CC0A             BREAK 10                   ; error trap
;       F4:       02               BYTE #X02
;       F5:       19               BYTE #X19                  ; INVALID-ARG-COUNT-ERROR
;       F6:       9A               BYTE #X9A                  ; RCX
NIL

这个函数有STC(设置进位)是因为它返回两个值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接