例如,如果一个循环正在运行,在每次迭代时调用'FOO,并且我在循环退出之前重新编译'FOO,会发生什么?
SBCL用于处理此类情况的具体机制是什么?
答案 0 :(得分:6)
SBCL是一个纯编译实现,所以很容易发现你的问题的答案:
* (defun foo (x) (print x))
FOO
* (describe 'foo)
COMMON-LISP-USER::FOO
[symbol]
FOO names a compiled function:
Lambda-list: (X)
Derived type: (FUNCTION (T) (VALUES T &OPTIONAL))
Source form:
(SB-INT:NAMED-LAMBDA FOO
(X)
(BLOCK FOO (PRINT X)))
* (disassemble (lambda ()(loop repeat 10 do (foo 1))))
; disassembly for (LAMBDA ())
; Size: 91 bytes. Origin: #x1002F7F564
; 64: BE14000000 MOV ESI, 20 ; no-arg-parsing entry point
; 69: EB3E JMP L1
; 6B: 0F1F440000 NOP
; 70: L0: 488BCE MOV RCX, RSI
; 73: 4883E902 SUB RCX, 2
; 77: 488BF1 MOV RSI, RCX
; 7A: 488D5C24F0 LEA RBX, [RSP-16]
; 7F: 4883EC18 SUB RSP, 24
; 83: BA02000000 MOV EDX, 2
; 88: 488975F8 MOV [RBP-8], RSI
; 8C: 488B057DFFFFFF MOV RAX, [RIP-131] ; #<FDEFINITION object for FOO>
; 93: B902000000 MOV ECX, 2
; 98: 48892B MOV [RBX], RBP
; 9B: 488BEB MOV RBP, RBX
; 9E: FF5009 CALL QWORD PTR [RAX+9]
; A1: 480F42E3 CMOVB RSP, RBX
; A5: 488B75F8 MOV RSI, [RBP-8]
; A9: L1: 4885F6 TEST RSI, RSI
; AC: 7FC2 JNLE L0
; AE: BA17001020 MOV EDX, 537919511
; B3: 488BE5 MOV RSP, RBP
; B6: F8 CLC
; B7: 5D POP RBP
; B8: C3 RET
; B9: 0F0B0A BREAK 10 ; error trap
; BC: 02 BYTE #X02
; BD: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; BE: 9A BYTE #X9A ; RCX
NIL
正如您所看到的,反汇编提到#<FDEFINITION object for FOO>
(而不是#<FUNCTION FOO>
返回的对象(fdefinition 'foo)
),因此,显然每个fdefinition
都会调用* (disassemble (lambda () (fdefinition 'foo)))
; disassembly for (LAMBDA ())
; Size: 31 bytes. Origin: #x1002FF99F4
; 9F4: 488B15A5FFFFFF MOV RDX, [RIP-91] ; 'FOO
; no-arg-parsing entry point
; 9FB: 488B05A6FFFFFF MOV RAX, [RIP-90] ; #<FDEFINITION object for FDEFINITION>
; A02: B902000000 MOV ECX, 2
; A07: FF7508 PUSH QWORD PTR [RBP+8]
; A0A: FF6009 JMP QWORD PTR [RAX+9]
; A0D: 0F0B0A BREAK 10 ; error trap
; A10: 02 BYTE #X02
; A11: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; A12: 9A BYTE #X9A ; RCX
NIL
* (disassemble (lambda () #.(fdefinition 'foo)))
; disassembly for (LAMBDA ())
; Size: 19 bytes. Origin: #x1003020214
; 14: 488B15A5FFFFFF MOV RDX, [RIP-91] ; #<FUNCTION FOO>
; no-arg-parsing entry point
; 1B: 488BE5 MOV RSP, RBP
; 1E: F8 CLC
; 1F: 5D POP RBP
; 20: C3 RET
; 21: 0F0B0A BREAK 10 ; error trap
; 24: 02 BYTE #X02
; 25: 19 BYTE #X19 ; INVALID-ARG-COUNT-ERROR
; 26: 9A BYTE #X9A ; RCX
NIL
迭代。
这可以通过比较这两个disassbblies来确认:
fdefinition
第一个肯定会调用(progn (sb-thread:make-thread (lambda () (sleep 5.1) (defun foo (x) (print (1+ x)))))
(dotimes (i 10) (sleep 1) (foo 1)))
,第二个肯定不会,第一个更接近循环的反汇编。
最后,可以使用Paulo Madeira的显式测试:
{{1}}
开始显示2。