在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.
答案 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_page
和unmap_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。如果我反汇编一个返回一个值的函数和一个返回多个值的函数,我可以看到CLC
和SEC
:
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
(设置进位)因为它返回两个值。