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

Pet*_*vaz 12 lisp assembly sbcl common-lisp

背景

在Windows上使用64位Steel Bank Common Lisp进行简单的身份识别时:

(defun a (x)
   (declare (fixnum x)) 
   (declare (optimize (speed 3) (safety 0))) 
  (the fixnum x))
Run Code Online (Sandbox Code Playgroud)

我发现反汇编如下:

* (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
Run Code Online (Sandbox Code Playgroud)

我理解的是:

mov rsp, rbp
pop rbp
ret  
Run Code Online (Sandbox Code Playgroud)

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

TEST AL, [#x200F0000]  // My understanding is that this sets flags based on bitwise and of AL and contents of memory 0x200F0000
Run Code Online (Sandbox Code Playgroud)

CLC // My understanding is that this clears the carry flag.
Run Code Online (Sandbox Code Playgroud)

质询

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

Sam*_*ard 6

正如反汇编程序提示的那样,TEST指令是一个安全点.它用于同步垃圾收集器的线程.安全点插入编译器知道线程处于安全状态以进行垃圾收集的位置.

safepoint的形式在compiler/x86-64/macros.lisp中定义:

#!+sb-safepoint
(defun emit-safepoint ()
  (inst test al-tn (make-ea :byte :disp sb!vm::gc-safepoint-page-addr)))
Run Code Online (Sandbox Code Playgroud)

您当然对未使用的操作结果是正确的.在这种情况下,SBCL对操作的副作用感兴趣.具体来说,如果包含地址的页面碰巧受到保护,则该指令会产生页面错误.如果页面可以被访问,指令只是浪费了非常小的时间量.我应该指出,这可能比简单地检查全局变量要快得多.

在Windows中,C函数map_gc_pageunmap_gc_page运行时/ Win32的os.c用于映射和取消映射的页面:

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));
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,我无法追踪页面错误处理程序,但一般的想法似乎是当需要一个集合时,unmap_gc_page将被调用.每个线程将继续运行,直到它遇到其中一个安全点,然后发生页面错误.大概是页面错误处理程序然后暂停该线程,然后当所有线程都被暂停时,垃圾收集运行,然后map_gc_page再次调用,并允许线程恢复.

学分文件向Anton Kovalenko介绍了这种机制.

在Linux和Mac OS X上,默认使用不同的同步机制,这就是为什么不在这些平台的默认构建上生成指令的原因.(我不确定PowerPC端口默认情况下是否使用安全点,但显然他们不使用x86指令).

另一方面,我不知道该CLC指令.


Geo*_*ler 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
Run Code Online (Sandbox Code Playgroud)

这个有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
Run Code Online (Sandbox Code Playgroud)

这个有STC(set Carry) 因为它返回两个值。