Subject: Re: How to write portable bitwise operation code? (or how not to think like a c hacker)
From: rpw3@rpw3.org (Rob Warnock)
Date: Sat, 07 Apr 2007 21:22:19 -0500
Newsgroups: comp.lang.lisp
Message-ID: <2sidnTt_LfRGzIXbnZ2dnUVZ_tKjnZ2d@speakeasy.net>
Liu Fung Sin <fungsin.lui@gmail.com> wrote:
+---------------
| rpw3@rpw3.org (Rob Warnock) wrote:
| > Upon further reflection, I noticed a *much* simpler way to do this...
...
| I did some comparisons, for educational purpose.
| It seems that (at least on 32-bit lisp) because everything is tagged
| and there's no 32-bit fixnum arithmetic, there are certain things that
| can't be done as efficiently as languages that are "closer" to the
| machine (e.g. C)
...
| (deftype ipv4-integer ()
|   '(integer 0 #xffffffff))
| 
| (defun ipv4-netmask-p (x)
|   (declare (optimize (speed 3) (safety 0)))
|   (declare (ipv4-integer x))
|   (= x (the ipv4-integer
|          (- #.(ash 1 32)
|             (the ipv4-integer
|               (logand x (the (integer #x-ffffffff 0) (- x))))))))
| 
| (disassemble #'ipv4-netmask-p)
| ...[elided]...
+---------------

Well, if you *only* want to do the 32-bit case, then my code is
a bit too general. Part of the problem you're seeing is the
(ASH 1 32), which is generating a 33-bit number, which forces
bignum arithmetic. (Oops.)

If one rewrites the expression to get rid of that, and uses
an explicit LOGAND to trim the result to 32 bits instead of
declarations or THE [which *don't* always trim them properly],
then things get quite a bit better [at least in CMUCL, which seems
to do a pretty good job of propagating (LOGAND #xffffffff EXPR)
down to the leaves of EXPR] -- not much bigger than your C code
if you ignore the differences in calling-sequence overheads:

> (defun ipv4-netmask-p (x)
    (declare (optimize (speed 3) (safety 0)))
    (declare (type (unsigned-byte 32) x))
    (and (plusp x) ; Needed 'cuz the tight version doesn't reject 0.
	 (= x (logand #xffffffff (1+ (- #xffffffff (logand x (- x))))))))

IPV4-NETMASK-P
> (disassemble *)
; Compiling LAMBDA (X): 
; Compiling Top-Level Form: 

48131020:       .ENTRY "LAMBDA (X)"(x)       ; (FUNCTION (#) (MEMBER T NIL))
      38:       POP     DWORD PTR [EBP-8]
      3B:       LEA     ESP, [EBP-32]
      3E:       MOV     EAX, EDX

      40:       TEST    AL, 3                ; [:NON-LOCAL-ENTRY]
      42:       JEQ     L0
      44:       MOV     EAX, [EAX-3]
      47:       JMP     L1
      49: L0:   SAR     EAX, 2

      4C: L1:   CMP     EAX, 0               ; No-arg-parsing entry point
                                             ; [:NON-LOCAL-ENTRY]
      4F:       JNBE    L4

;;; [11] (AND (PLUSP X) (= X (LOGAND 4294967295 #)))

      51:       MOV     EDX, #x28F0000B      ; NIL
                                             ; [:BLOCK-START]

      56: L2: L3:MOV    ECX, [EBP-8]         ; [:BLOCK-START]
      59:       MOV     EAX, [EBP-4]
      5C:       ADD     ECX, 2
      5F:       MOV     ESP, EBP
      61:       MOV     EBP, EAX
      63:       JMP     ECX

;;; [15] (1+ (- 4294967295 (LOGAND X #)))

      65: L4:   MOV     ECX, EAX             ; [:BLOCK-START]
      67:       NEG     ECX
      69:       MOV     EDX, EAX
      6B:       AND     EDX, ECX
      6D:       MOV     ECX, 4294967295
      72:       SUB     ECX, EDX
      74:       INC     ECX
      75:       CMP     ECX, EAX
      77:       JEQ     L5

;;; [13] (= X (LOGAND 4294967295 (1+ #)))

      79:       MOV     EDX, #x28F0000B      ; NIL
                                             ; [:BLOCK-START]
      7E:       JMP     L3

      80: L5:   MOV     EDX, 686817319       ; T
                                             ; [:BLOCK-START]
      85:       JMP     L3
      87:       NOP
> 


-Rob

-----
Rob Warnock			<rpw3@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607