3 ms·
Plenty of implementations of dialects of Lisp have compilers that produce machine code. I would even say that for Common Lisp implementations it is more common
by omaranto 9y ago
Plenty of implementations of dialects of Lisp have compilers that produce machine code. I would even say that for Common Lisp implementations it is more common to have a compiler than to not have one. There are several great Scheme compilers too, and Racket has a JIT.
Here's a sample session with SBCL, where I define a function, which is immediately compiled, and then I request a disassembly:
CL-USER> (defun foo (n m) (* (1+ m) n))
FOO
CL-USER> (disassemble #'foo)
; disassembly for FOO
; Size: 48 bytes. Origin: #x1007CED6F3
; 6F3: 498B4C2460 MOV RCX, [R12+96] ; thread.binding-stack-pointer
; no-arg-parsing entry point
; 6F8: 48894DF8 MOV [RBP-8], RCX
; 6FC: 488B55E8 MOV RDX, [RBP-24]
; 700: BF02000000 MOV EDI, 2
; 705: 41BBC0010020 MOV R11D, 536871360 ; GENERIC-+
; 70B: 41FFD3 CALL R11
; 70E: 488B7DF0 MOV RDI, [RBP-16]
; 712: 41BBA0020020 MOV R11D, 536871584 ; GENERIC-*
; 718: 41FFD3 CALL R11
; 71B: 488BE5 MOV RSP, RBP
; 71E: F8 CLC
; 71F: 5D POP RBP
; 720: C3 RET
; 721: CC10 BREAK 16 ; Invalid argument count trap
- white-flame 9y agoAnd if you declare types in SBCL as fixnum (register-sized ints): CL-USER> (disassemble #'foo) ; disassembly for FOO ; Size: 19 bytes. Origin: #x100784BCCB ; CB: 48FFC0 INC RAX ; no-arg-parsing entry point ; CE: 480FAFC1 IMUL RAX, RCX ; D2: 48D1E0 SHL RAX, 1 ; D5: 488BD0 MOV RDX, RAX ; D8: 488BE5 MOV RSP, RBP ; DB: F8 CLC ; DC: 5D POP RBP ; DD: C3 RET As far as overhead goes, the SHL makes room for tag bits (and notice that's only done once, not per math op), and CLC indicates that the function doesn't return multiple values. That is not significant overhead, and will absolutely destroy reference Python in speed.
- omaranto 9y agoMaybe it's worth pointing out that to get that code like that from SBCL it's not enough to declare that m and n are fixnums, you also need to promise the compiler that the multiplication won't overflow: (defun foo (n m) (declare (type fixnum m n) (optimize (safety 0))) (the fixnum (* (1+ m) n)))
- white-flame 9y agoThe reason I didn't list it is because there's so many different ways to do it. The optimization declaration can be made globally, or per-file; declaring the type of the foo function (including parameters & return values) externally will fully cover this function without having anything additional in the body; there's helper macros to explicitly manage static types, etc. So the type declarations don't need to be intrusive into the expressions themselves, and can be as transparent as you want. Of course, type inference also means that you don't have to specify every single type, while still getting the speed & checking benefit of fully typed code.
- omaranto 9y agoCould you show a way to get that code to be generated without any declarations in the body of the function? I tried something like: (declaim (ftype (function (fixnum fixnum) fixnum) foo) (optimize (safety 0))) (defun foo (m n) (* (1+ m) n)) That didn't optimize as fully.
- white-flame 9y agoYou forgot (optimize (speed 3))
- omaranto 9y agoI did try with speed 3. Do you mean that: (declaim (ftype (function (fixnum fixnum) fixnum) foo) (optimize (safety 0) (speed 3)) (defun foo (m n) (* (1+ m) n)) should have produced the optimized code (without any generic-*)? It didn't in my testing on SBCL 1.3.18.