From: Vladimir V. Zolotych
Subject: type declarations
Date: 
Message-ID: <3AB108A1.F27DD76F@eurocom.od.ua>
This is a multi-part message in MIME format.
--------------21F1A972A7A89862CC6A8014
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit

	Hello

Using CMUCL 18c

- Type declarations

  How I can see usefulness of the type declarations in more 
  obvious way ? In other words, how I make sure the declarations 
  I've made help compiler to generate better code ?

  (defun foo (x y)
    (declare (fixnum x y))
    (the fixnum (+ x y)))

  (defun foo2 (x y)
    (+ x y))

  I've try to (DISASSEMBLE 'FOO) and (DISASSEMBLE 'FOO2). I took a
  glance on both versions. The first version is longer (65 lines against
  25 for foo2. Beside that I could say nothing.

  Quote from "http://www.alu.org/table/style.htm" "Check the code
  produced by the compiler by using the disassemble function" I've
  attached FOO.ASM (produced by CMUCL compiler) in hope you give me
  some introduction to understanding generated code.

- Locating the syntax errors

  Suppose I have "long" function, like the following

  (defun parse-date (string &optional (start 0) end)
    (declare (type string string)
             (type fixnum start)
             (type (or fixnum null) end))
    (let ((pos start)
          (c nil))
      (declare (type fixnum pos)
               (type character c))
      (labels ((bad-date ()

  ;;...........[snip].............
               (digit-code (c)
                 (declare character c)
  ;;                      ^ should be (character  c)
                 (- (char-code c) #.(char-code #\0))))
       (let (day mo year)
         (next-char)
  ;;............[snip].............
         (when c (bad-date))
         (encode-universal-time 0 0 0 day mo year)))))

  The total length of the function - 50 lines. There is "syntax" error
  in it.  When I try to run it I see the error "Execution of a form
  compiled with errors:" with the whole form following.  Could I narrow
  error location some way ? I just cut off pieces of code now.

- "Unable to optimize"

  I see the following notes from compiler:

  (defun parse-float (string &key (start 0) end)
    (declare (type string string)
             (type fixnum start)
             (type (or fixnum null) end)
             (optimize (speed 3) (space 2) (safety 0) (debug 0)))
    (let ((point start)
          (c nil))
      (declare (type fixnum point))
      (labels ((bad-float ()
                 (error "Failed to parse a float out of ~S."
                        (subseq string start end)))
  ;;                    ^^^^^^^^^^^^^^^^^^^^^^^^^
  ;;Note: Unable to optimize due to type uncertainty:
  ;;      The first argument is a BASE-STRING, not a SIMPLE-BASE-STRING.

               (next-char (&optional (eof-error-p t))
                 (cond ((= point (or end (length string)))
  ;;                                     ^^^^^^^^^^^^^^^
  ;;Note: Unable to optimize due to type uncertainty:
  ;;      The first argument is a BASE-STRING, not a (SIMPLE-ARRAY *
(*)).

  ;; ........... [snip] ..............
           (return-from parse-float (read-from-string string nil nil
                                                      :start start
                                                      :end point))))))

  Should I change declaration (TYPE STRING STRING) in the beginning 
  although it seems reasonable to me ?

- PARSE-FLOAT

  Which is more correct
  a * (parse-float "0")
    Error in function BAD-FLOAT:  Failed to parse a float ....
  b * (parse-float "0")
    0

- What does phrase "CASE statements can be vectorized if the keys are
  consecutive numbers." mean ?  E.g. 'vectorize'. 
  (http://www.alu.org/table/style.htm)

- Type declarations 
  I need to distinguish two cases: 1) '((1 . 20) (2
  . 30) ...)  and just (10 . 20) (one pair instread of the list of
  pairs). I've write the following:

  (defun valid-cons (x)
    (and (typep (car x) '(integer 0 100))
         (typep (cdr x) '(integer 0 100))))

  (deftype my-cons ()
    `(and cons (satisfies valid-cons)))

  Could you tell me how you would write this ? I feel weakness in
  writing such thins.

- Example of TRACE output

  0: (ADD-PATTERN "         " ((0 . 0) (0 . 0) (0 . 0) (0 . 0) (0 . 1)
...))
  0: ADD-PATTERN returned ((0 . 0) (0 . 0) (0 . 0) (0 . 0) (0 . 1) ...)

  I'd like to see contents of corresponding data instead of '...'. Is
  it possible ?

	Best regards

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
--------------21F1A972A7A89862CC6A8014
Content-Type: text/plain; charset=us-ascii;
 name="foo.asm"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="foo.asm"

48612040:       .ENTRY FOO(x y)              ; (FUNCTION (FIXNUM FIXNUM) FIXNUM)
     058:       POP   DWORD PTR [EBP-8]
     05B:       LEA   ESP, [EBP-32]

     05E:       CMP   ECX, 8
     061:       JNE   L1
     063:       TEST  DL, 3
     066:       JNE   L2
     068:       TEST  EDI, 3
     06E:       JNE   L3
     070:       MOV   EBX, EDX
     072:       MOV   EAX, EBX               ; No-arg-parsing entry point
     074:       SAR   EAX, 2
     077:       MOV   ECX, EDI
     079:       SAR   ECX, 2
     07C:       ADD   EAX, ECX
     07E:       MOV   EDX, EAX
     080:       SHL   EDX, 1
     082:       JO    L5
     084:       SHL   EDX, 1
     086:       JO    L5
     088: L0:   TEST  DL, 3
     08B:       JNE   L8
     08D:       MOV   ECX, [EBP-8]
     090:       MOV   EAX, [EBP-4]
     093:       ADD   ECX, 2
     096:       MOV   ESP, EBP
     098:       MOV   EBP, EAX
     09A:       JMP   ECX
     09C:       NOP
     09D:       NOP
     09E:       NOP
     09F:       NOP
     0A0: L1:   BREAK 10                     ; Error trap
     0A2:       BYTE  #x02
     0A3:       BYTE  #x19                   ; INVALID-ARGUMENT-COUNT-ERROR
     0A4:       BYTE  #x4D                   ; ECX
     0A5: L2:   BREAK 10                     ; Error trap
     0A7:       BYTE  #x02
     0A8:       BYTE  #x0A                   ; OBJECT-NOT-FIXNUM-ERROR
     0A9:       BYTE  #x8E                   ; EDX
     0AA: L3:   BREAK 10                     ; Error trap
     0AC:       BYTE  #x04
     0AD:       BYTE  #x0A                   ; OBJECT-NOT-FIXNUM-ERROR
     0AE:       BYTE  #xFE, #xCE, #x01       ; EDI
     0B1: L4: L5:MOV  BYTE PTR [#x280001D4], 0
     0B8:       MOV   BYTE PTR [#x280001BC], 4
     0BF:       MOV   EDX, 8
     0C4:       ADD   EDX, [#x80696C4]
     0CA:       CMP   EDX, [#x8069698]
     0D0:       JBE   L6
     0D2:       CALL  #x80536EC
     0D7: L6:   XCHG  EDX, [#x80696C4]
     0DD:       MOV   DWORD PTR [EDX], 266
     0E3:       LEA   EDX, [EDX+7]
     0E6:       MOV   [EDX-3], EAX
     0E9:       MOV   BYTE PTR [#x280001BC], 0
     0F0:       CMP   BYTE PTR [#x280001D4], 0
     0F7:       JEQ   L7
     0F9:       BREAK 9                      ; Pending interrupt trap
     0FB: L7:   JMP   L0
     0FD: L8:   BREAK 10                     ; Error trap
     0FF:       BYTE  #x02
     100:       BYTE  #x0A                   ; OBJECT-NOT-FIXNUM-ERROR
     101:       BYTE  #x8E                   ; EDX

--------------21F1A972A7A89862CC6A8014--

From: Raymond Toy
Subject: Re: type declarations
Date: 
Message-ID: <4nae6n53uk.fsf@rtp.ericsson.se>
>>>>> "Vladimir" == Vladimir V Zolotych <······@eurocom.od.ua> writes:

    Vladimir> 	Hello
    Vladimir> Using CMUCL 18c

    Vladimir> - Type declarations

    Vladimir>   How I can see usefulness of the type declarations in more 
    Vladimir>   obvious way ? In other words, how I make sure the declarations 
    Vladimir>   I've made help compiler to generate better code ?

    Vladimir>   (defun foo (x y)
    Vladimir>     (declare (fixnum x y))
    Vladimir>     (the fixnum (+ x y)))

    Vladimir>   (defun foo2 (x y)
    Vladimir>     (+ x y))

If speed is important, then you can try compiling with (speed 3).
Then CMUCL will produce notes about what it's doing.  Then it's clear
that your declarations have some affect.

  (defun foo (x y)
    (declare (fixnum x y) (optimize (speed 3)))
    (the fixnum (+ x y)))

  (defun foo2 (x y)
    (declare (optimize (speed 3)))
    (+ x y))

  (compile 'foo) =>
; Compiling LAMBDA (X Y): 

; In: LAMBDA (X Y)

;   FUNCTION
; Note: Doing signed word to integer coercion (cost 20), for:
;     The first argument of CHECK-FIXNUM.

This note happens because x+y may not be a fixnum even if x and y are.

  (compile 'foo2) =>
; In: LAMBDA (X Y)

;   +
; Note: Unable to optimize due to type uncertainty:
;     The first argument is a NUMBER, not a RATIONAL.
;     The second argument is a NUMBER, not a FLOAT.
; 
; Note: Unable to optimize due to type uncertainty:
;     The first argument is a NUMBER, not a FLOAT.
;     The second argument is a NUMBER, not a RATIONAL.
; 
; Note: Unable to optimize due to type uncertainty:
;     The first argument is a NUMBER, not a SINGLE-FLOAT.
;     The second argument is a NUMBER, not a DOUBLE-FLOAT.
; 
; Note: Unable to optimize due to type uncertainty:
;     The first argument is a NUMBER, not a DOUBLE-FLOAT.
;     The second argument is a NUMBER, not a SINGLE-FLOAT.
; 
; Note: Forced to do GENERIC-+ (cost 10).
;     Unable to do inline complex float/float arithmetic (cost 1) because:
;     The first argument is a NUMBER, not a SINGLE-FLOAT.
;     The second argument is a NUMBER, not a (COMPLEX SINGLE-FLOAT).
;     The result is a NUMBER, not a (COMPLEX SINGLE-FLOAT).
;     Unable to do inline fixnum arithmetic (cost 2) because:
;     The first argument is a NUMBER, not a FIXNUM.
;     The second argument is a NUMBER, not a FIXNUM.
;     The result is a NUMBER, not a FIXNUM.
;     etc.


    Vladimir> - "Unable to optimize"

    Vladimir>   I see the following notes from compiler:

    Vladimir>   (defun parse-float (string &key (start 0) end)
    Vladimir>     (declare (type string string)
    Vladimir>              (type fixnum start)
    Vladimir>              (type (or fixnum null) end)
    Vladimir>              (optimize (speed 3) (space 2) (safety 0) (debug 0)))
    Vladimir>     (let ((point start)
    Vladimir>           (c nil))
    Vladimir>       (declare (type fixnum point))
    Vladimir>       (labels ((bad-float ()
    Vladimir>                  (error "Failed to parse a float out of ~S."
    Vladimir>                         (subseq string start end)))
    Vladimir>   ;;                    ^^^^^^^^^^^^^^^^^^^^^^^^^
    Vladimir>   ;;Note: Unable to optimize due to type uncertainty:
    Vladimir>   ;;      The first argument is a BASE-STRING, not a SIMPLE-BASE-STRING.

This means exactly that.  Better code results if string is a
simple-base-string.

    Vladimir>   Should I change declaration (TYPE STRING STRING) in the beginning 
    Vladimir>   although it seems reasonable to me ?

Only if it makes sense to do so.  You could say string is a
simple-base-string if it really is.

    Vladimir> - Example of TRACE output

    Vladimir>   0: (ADD-PATTERN "         " ((0 . 0) (0 . 0) (0 . 0) (0 . 0) (0 . 1)
    Vladimir> ...))
    Vladimir>   0: ADD-PATTERN returned ((0 . 0) (0 . 0) (0 . 0) (0 . 0) (0 . 1) ...)

    Vladimir>   I'd like to see contents of corresponding data instead of '...'. Is
    Vladimir>   it possible ?

Set *print-level* and/or *print-length* to be bigger numbers?

Ray
From: Vladimir V. Zolotych
Subject: Re: type declarations
Date: 
Message-ID: <3AB1E797.C368063A@eurocom.od.ua>
Raymond Toy wrote:
> 
> Set *print-level* and/or *print-length* to be bigger numbers?

(defun foo (a) (bar a))
(defun bar (a) a)
(trace foo)
(trace bar)
(let ((a (loop repeat 20 collecting '(1 . 1))))
  (let ((*print-length* nil))
    (foo a)))
This produce
0: (FOO ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...))
    1: (BAR ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...))
    1: BAR returned ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...)
  0: FOO returned ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...)
((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1)
 (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1)
 (1 . 1) (1 . 1))
* 
HS says that no limit on number of printed items on the same level
when *print-length* is nil.

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Kent M Pitman
Subject: Re: type declarations
Date: 
Message-ID: <sfwy9u594v9.fsf@world.std.com>
"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> Raymond Toy wrote:
> > 
> > Set *print-level* and/or *print-length* to be bigger numbers?
> 
> (defun foo (a) (bar a))
> (defun bar (a) a)
> (trace foo)
> (trace bar)
> (let ((a (loop repeat 20 collecting '(1 . 1))))
>   (let ((*print-length* nil))
>     (foo a)))
> This produce
> 0: (FOO ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...))
>     1: (BAR ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...))
>     1: BAR returned ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...)
>   0: FOO returned ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) ...)
> ((1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1)
>  (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1) (1 . 1)
>  (1 . 1) (1 . 1))
> * 
> HS says that no limit on number of printed items on the same level
> when *print-length* is nil.

But it doesn't require all programs never to bind this variable.
(If it did, the variable would be useless.)

In order to protect itself from unexpected circular structure, most 
implementations have a default binding of *print-length* that they
do around stuff printed by TRACE.  Usually you can find this variable
by doing (apropos "print-length") in the vendor's implementation, since
commonly it's called something like *TRACE-PRINT-LENGTH*.  While this
is not behavior prescribed by the standard, it's permitted by the
standard and so is conforming.  I do think it would have been better
if the standard actually specified the names of these variables, but
the world is not perfect.

Btw, you may also find that DESCRIBE and STEP use similar tricks, and
that each may have its own variable (well, that might depend on the
implementation--I haven't done a survey).
From: Vladimir V. Zolotych
Subject: Re: type declarations
Date: 
Message-ID: <3AB24006.12EC007A@eurocom.od.ua>
Kent M Pitman wrote:
> 
> .....  Usually you can find this variable
> by doing (apropos "print-length")

Yes, this was a help
The following give me what I wanted:

(let ((a (loop repeat 20 collecting '(1 . 1))))
  (let ((debug:*debug-print-length* nil))
    (foo a)))

-- 
Vladimir Zolotych                         ······@eurocom.od.ua
From: Kalle Olavi Niemitalo
Subject: Re: type declarations
Date: 
Message-ID: <87wv9qvgse.fsf@PC486.y2000.kon.iki.fi>
I'm not exactly familiar with the code generation of CMUCL, but
the x86 code looks quite simple, so...

"Vladimir V. Zolotych" <······@eurocom.od.ua> writes:

> 48612040:       .ENTRY FOO(x y)              ; (FUNCTION (FIXNUM FIXNUM) FIXNUM)
>      058:       POP   DWORD PTR [EBP-8]

Some initialization.

>      05B:       LEA   ESP, [EBP-32]

Allocate local variables.

> 
>      05E:       CMP   ECX, 8
>      061:       JNE   L1

Check that exactly two arguments were given, jump to L1 if not.
The number is quadrupled because the two least significant bits
are used for indicating that it's a fixnum.

>      063:       TEST  DL, 3
>      066:       JNE   L2
>      068:       TEST  EDI, 3
>      06E:       JNE   L3

Check that the arguments are fixnums, jump to L2 or L3 if not.

>      070:       MOV   EBX, EDX
>      072:       MOV   EAX, EBX               ; No-arg-parsing entry point
>      074:       SAR   EAX, 2
>      077:       MOV   ECX, EDI
>      079:       SAR   ECX, 2

Shift out tag bits from the fixnums, so they become native integers.

>      07C:       ADD   EAX, ECX

Add them up.  There is no overflow check here; it is not
necessary because removing the tag bits has left two bits of
space, and the sum of two n-bit numbers always fits in n+1 bits.

>      07E:       MOV   EDX, EAX
>      080:       SHL   EDX, 1
>      082:       JO    L5
>      084:       SHL   EDX, 1
>      086:       JO    L5

Shift the tag bits back in, jump to L5 if the sum is so large
they don't fit.

>      088: L0:   TEST  DL, 3
>      08B:       JNE   L8

Check that the tag bits just inserted show the value is a fixnum.
This check would normally be unnecessary, but it seems it can
also be reached via L5.  Perhaps it is needed if the addition
results in an error and you tell the lisp to use a different
value.

>      08D:       MOV   ECX, [EBP-8]
>      090:       MOV   EAX, [EBP-4]
>      093:       ADD   ECX, 2
>      096:       MOV   ESP, EBP
>      098:       MOV   EBP, EAX
>      09A:       JMP   ECX

Restore the stack frame and return to the caller.

>      09C:       NOP
>      09D:       NOP
>      09E:       NOP
>      09F:       NOP

Padding to get the following code nicely aligned so the processor
can read it in more efficiently.  This is never executed, as
there is a JMP above it and no label for entry.

>      0A0: L1:   BREAK 10                     ; Error trap
>      0A2:       BYTE  #x02
>      0A3:       BYTE  #x19                   ; INVALID-ARGUMENT-COUNT-ERROR
>      0A4:       BYTE  #x4D                   ; ECX

This and the others below are error handlers to which the
function jumps if something odd happens.  They are not run in
normal cases.