Avoiding float to pointer coercion in Common Lisp

2019-07-23 23:07发布

问题:

I use SBCL (64-bit v1.4.0) for numerical calculation. After enabling optimization, following compiler note appears:

note: doing float to pointer coercion (cost 13) to "<return value>"

The code I use is as follows:

(defun add (a b)
  (declare (optimize (speed 3) (safety 0)))
  (declare (double-float a b))
  (the double-float (+ a b)))

I've also tried ftype and got the same note.

On the other hand, following code doesn't show the note:

(defun add-fixnum (a b)
  (declare (optimize (speed 3) (safety 0)))
  (declare (fixnum a b))
  (the fixnum (+ a b)))

I think double-float and fixnum are both 64 bits wide. Why can not SBCL return a double-float value via a register like C language? And are there any way to avoid float to pointer coercion without inline expansion?

回答1:

The problem is that Lisp data is dynamically typed, and the return value of a function has to include the type information. The type tag in most implementations is stored in the low-order bits of a value.

This allows a special optimization for fixnums. Their type tag is all zeroes, and the value is the integer shifted left by the number of bits in the type tag. When you add these values, the result still has zeroes in the tag bits, so you can perform arithmetic on the values using normal CPU operations.

But this doesn't work for floating point values. After performing the CPU operations, it has to add the type tag to the value. This is what it means by "float to pointer coercion" (a more common word for it in many languages is "boxing").

Declaring the return type doesn't avoid this, because the callers don't necessarily have access to the declarations -- Lisp allows you to compile the callers in a separate compilation unit than the functions they call.

If you declare the function INLINE, then this doesn't need to be done, because the callers know the type it's returning, and the hardware value can be returned directly to them without adding the tag.

A more detailed explanation can be found in this ancient comp.lang.lisp thread. It's referring to CMUCL, which is what SBCL is derived from (notice that the wording of the warning is exactly the same).