背景
在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.
问题
最佳答案
正如反汇编程序所暗示的那样,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
指令一无所知。关于assembly - 了解SBCL进入/退出组件样板代码,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21859232/