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--
>>>>> "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
"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).
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
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.