Andrzej,
I'm convinced this is a good idea. If you have the time/budget, I suggest you open a pull request. Sooner or later we'll need documentation and tests.
Cheers, Luís
On Wed, Nov 8, 2017 at 4:50 PM, Andrzej Walczak andrzejwalczak@google.com wrote:
Hello Luis,
Indeed, I am guilty as well of writing a compiler-macro based Lisp-form compiler - as hinted in the pro@c-l.net post :) It was one of the motivations to add FTYPEs everywhere. Thank you for the interesting pointer.
As stated in the quoted comment, double-float boxing is one of the reasons why we inline CFFI stubs. It's not like SBCL could chose a different call convention depending on known type declarations. Once inlined, there will be no difference for FABS-1 and FABS-2 - with or without type of ftype declarations. Thank you for the excellent example illustrating this classic issue.
The FTYPEs help SBCL produce better code if the double-float function stubs are not inlined (for some reason). Also, going back to the post you have mentioned, having FTYPEs on CFFI could help compiler-macros take informed decisions about code transformations.
E.g. (quickly typed - might not compile)
(declaim (ftype (function (double-float double-float) (values double-float &optional)) pow) (inline pow)) (defcfun pow :double (base :double) (exp :double))
(defun test (&rest nums) (sum (bind #'pow 2) nums))
Could expand to:
(defun test (&rest nums) (flet ((#:f1 (#:e1) (declare (double-float #:e1)) (pow 2 #:e1))) (declare (ftype (function (double-float) (values double-float &optional)) #:f1) (dynamic-extent #'#:f1)) (let ((#:a1 (first nums))) (declare (double-float #:a1)) (dolist (#:n1 (rest nums) #:a1) (setf #:a1 (+ #:a1 (funcall #'#:f1 #:n1)))))))
Which should allow the compiler to choose an unboxed representation for passing arguments to the internal #'#:f1. BTW: I am sure SBCL would do just fine with just 1/3rd of the declarations above - but unsure which 1/3rd ... this time of year.
Cheers,
On Tue, Nov 7, 2017 at 7:12 PM, Luís Oliveira luismbo@gmail.com wrote:
On Mon, Nov 6, 2017 at 5:10 PM, Andrzej Walczak andrzejwalczak@google.com wrote:
After some thoughts, I agree, that the inlining control is not a fully-baked idea, yet.
Incidentally, this recent pro@c-l.net message makes me a whole lot less skeptical about your suggested approach: https://mailman.common-lisp.net/pipermail/pro/2017-November/001464.html. The introspect-environment library seems to provide a nice portable API for inspecting optimization policies. Alas, there doesn't seem to be away to define your own declarations; I was hoping we might be able to something like (declaim (inline-cffi-functions <boolean>)) or something along those lines.
I'll split the patch into INLINE and FTYPE parts.
Sounds like a good idea.
You may ask why we have decided to inline (almost) every of the DEFCFUNs? CFFI interfaces to C - obviously - and a lot of code in C deals with double-floats and 64-bit integers. Without the stubs being inline, every call to C and back produces boxed values, impacting the performance.
Right. Float boxing; that's a classic. But, I (perhaps naively) didn't expect boxing to happen even without the declarations. Take the following example:
(in-package :cffi)
(declaim (optimize (speed 3) (space 0) (safety 0) (debug 0)))
(declaim (inline fabs-1)) (defun fabs-1 (x) (foreign-funcall "fabs" :double x :double))
(declaim (inline fabs-2)) (defun fabs-2 (x) (declare (double-float x)) (foreign-funcall "fabs" :double x :double))
(defun foo-1 (x) (floatp (fabs-1 x)))
(defun foo-2 (x) (floatp (fabs-2 x)))
I expected FABS-1 and FABS-2 to be identical. (FOREIGN-FUNCALL eventually expands to ALIEN-FUNCALL and I was expecting that would provide all the type information SBCL's compiler might need.) The disassembly shows that FAB-2 is one instruction shorter. But disassembling FOO-1 and FOO-2 shows that they're identical.
Perhaps this example is too contrived. Do you have a better one?
Cheers,
-- Luís Oliveira http://kerno.org/~luis/
-- Andrzej Walczak (Google/ITA Software Engineer)