minya
minya

Reputation: 325

When does SBCL replace the compiled function with newer version if that function is in use?

For instance, if a loop is running that calls 'FOO at every iteration, and I recompile 'FOO before the loop exits, what happens?

What are the specific mechanism SBCL uses to handle such situations?

Upvotes: 2

Views: 296

Answers (1)

sds
sds

Reputation: 60004

SBCL is a compile-only implementation, so the answer to your question is easy to discover:

* (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

As you can see, the disassembly mentions #<FDEFINITION object for FOO> (as opposed to a the object #<FUNCTION FOO> returned by (fdefinition 'foo)), so, apparently, fdefinition is called on each iteration.

This can be confirmed by comparing these two disassmeblies:

* (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

the first definitely calls fdefinition and the second definitely does not, and the first is closer to the disassembly of the loop.

Finally, one can use the explicit test by Paulo Madeira:

(progn (sb-thread:make-thread (lambda () (sleep 5.1) (defun foo (x) (print (1+ x))))) 
       (dotimes (i 10) (sleep 1) (foo 1))) 

starts showing 2.

Upvotes: 6

Related Questions