了解SBCL进/出装配锅炉板代码

时间:2014-02-18 16:11:08

标签: assembly lisp common-lisp sbcl

背景

在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在从函数返回之前清除进位标志?

2 个答案:

答案 0 :(得分:6)

正如反汇编程序提示的那样,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指令。

答案 1 :(得分:4)

我对TEST AL, [#x200F0000]一无所知,但我相信CLC适用于返回一个值的函数。 SBCL Internals Manual, "Unknown-Values Returns",建议函数在返回多个值时设置进位标志,如果返回一个值则清除进位标志。

我正在使用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(设置进位)因为它返回两个值。