rwiker
1/5/2016 8:50:00 PM
Jim Newton <jimka.issy@gmail.com> writes:
> It looks like even promising that the integer is between 0 and 1000 also fails
> to allow the compiler to use the post-increment instruction. (at least in SBCL)
>
> (disassemble (lambda (i)
> (declare (type (integer 0 1000) i)
> (optimize (speed 3)
> (debug 0)
> (safety 0)))
> (prog1 i (incf i))))
> ; disassembly for (LAMBDA (I))
> ; Size: 19 bytes. Origin: #x12FBB34052
> ; 52: 488BC1 MOV RAX, RCX ; no-arg-parsing entry point
> ; 55: 4883C102 ADD RCX, 2
> ; 59: 488BC8 MOV RCX, RAX
> ; 5C: 488BD1 MOV RDX, RCX
> ; 5F: 488BE5 MOV RSP, RBP
> ; 62: F8 CLC
> ; 63: 5D POP RBP
> ; 64: C3 RET
> NIL
A few things:
1) You're incrementing a value passed as a parameter, then returning the
previous value. The value will only be updated in the caller if the
value is passed by reference (and maybe not even then). In that case, a
sufficiently smart compiler may also skip the increment.
2) Using a fixnum instead of a restricted integer may be better for
encouraging the compiler to use simple register operations.
3) If you're working on tagged values, a simple inc is actually
implementing as adding a value which depends on the tag length. E.g, if
the tag length is 3 bits and even/odd integers have the tags 000/100, an
inc (or 1+) operations may look like "add eax,4" (the latter is from
running the form below through Lispworks 7.0 (32-bit, OSX)).
(disassemble
(compile nil
(lambda (i)
(declare (type (integer 0 1000) i)
(optimize (speed 3)
(debug 0)
(safety 0)))
(prog1 (incf i)))))
=>
0: 55 push ebp
1: 89E5 move ebp, esp
3: 83C004 add eax, 4
6: FD std
7: C9 leave
8: C3 ret
9: 90 nop
However, if you return i instead, the disassembly looks like
(disassemble
(compile nil
(lambda (i)
(declare (type (integer 0 1000) i)
(optimize (speed 3)
(debug 0)
(safety 0)))
(prog1 i (incf i)))))
=>
0: 55 push ebp
1: 89E5 move ebp, esp
3: FD std
4: C9 leave
5: C3 ret
No sign of an increment, because the new value is not used.