3 ms·
FWIW, SBCL is pretty good at optimizing away dynamic type checks if you help it out. Here are some examples under: (declaim (optimize (speed 2))) First e
by skulk 1y ago
FWIW, SBCL is pretty good at optimizing away dynamic type checks if
you help it out.
Here are some examples under:
(declaim (optimize (speed 2)))
First example is a generic multiplication. x and y could be _any_ type at all.
(defun fn (x y) (* x y))
If we disassemble this function, we get the following:
; disassembly for FN
; Size: 34 bytes. Origin: #x1001868692 ; FN
; 92: 488975F8 MOV [RBP-8], RSI
; 96: 4C8945F0 MOV [RBP-16], R8
; 9A: 498BD0 MOV RDX, R8
; 9D: 488BFE MOV RDI, RSI
; A0: FF142540061050 CALL [#x50100640] ; SB-VM::GENERIC-*
; A7: 4C8B45F0 MOV R8, [RBP-16]
; AB: 488B75F8 MOV RSI, [RBP-8]
; AF: C9 LEAVE
; B0: F8 CLC
; B1: C3 RET
; B2: CC0F INT3 15 ; Invalid argument count trap
Note that it calls `GENERIC-*` which probably checks a lot of things and has a decent overhead.
Now, if we tell it that x and y are bytes, it's going to give us much simpler code.
(declaim (ftype (function ((unsigned-byte 8) (unsigned-byte 8)) (unsigned-byte 16)) fn-t))
(defun fn-t (x y) (* x y))
The resulting code uses the imul instruction.
; disassembly for FN-T
; Size: 15 bytes. Origin: #x1001868726 ; FN-T
; 26: 498BD0 MOV RDX, R8
; 29: 48D1FA SAR RDX, 1
; 2C: 480FAFD7 IMUL RDX, RDI
; 30: C9 LEAVE
; 31: F8 CLC
; 32: C3 RET
; 33: CC0F INT3 15 ; Invalid argument count trap*
- matheusmoreira 1y agoCan SBCL actually check at compile time that the arguments to fn-t are bytes? I wonder how that works with Lisp's extreme dynamism. Also wondering about the calling convention it uses.
- reikonomusha 1y agoIt can detect simple-ish instances, like calling (fn-t "hello" "world") But a good rule-of-thumb is that these compile-time type errors are more of a courtesy, rather than a guarantee. As soon as you abstract over fn-t with another function, like so: (defun g (x y) (fn-t x y)) and proceed to use g in your code, all the static checking won't happen anymore, because as far as g is concerned, it can take any input argument types. CL-USER> (defun will-it-type-error? () (g "x" "y")) ;; compilation WILL-IT-TYPE-ERROR? successful No compile-time warning is issued. Contrast with Coalton: COALTON-USER> (coalton-toplevel (declare fn-t (U8 -> U8 -> U8)) (define (fn-t x y) (* x y)) (define (g x y) (fn-t x y)) (define (will-it-type-error?) (g "hello" "world"))) error: Type mismatch --> <macroexpansion>:8:7 | 8 | (G "hello" "world"))) | ^^^^^^^ Expected type 'U8' but got 'STRING' [Condition of type COALTON-IMPL/TYPECHECKER/BASE:TC-ERROR]