dedieu@lifia.imag.fr (Eric Dedieu ) (03/13/91)
Hello, Another bug found in Lucid Common List 3.0.1 on sun 3 (It was a good day for me) It seems to be the "dotimes" instruction that wants to try our sense of humor when compiled in production mode The sun4 version works correctly -- I was wondering why my program didn't do the same thing on sun3, when it fell on an floating-point-underflow-error. I examined this and found a bug, but I'd still like to know what is exactly a floating-point UNDERFLOW error... Full info about what I found: > ;;; float vector constructor (defmacro vf (&body x) `(make-array ,(length x) :initial-contents (list ,.x) :element-type 'float)) Vf > ;;; A well-known distance (defun dist1 (v1 v2 &aux (res 0.0)) (declare (type (simple-array float (*)) v1 v2) (float res)) (dotimes (i 2) ; originally (length v1) (incf res (the float (abs (the float (- (the float (aref v1 i)) (the float (aref v2 i)))))))) res) Dist1 > ;;; Examples (dist1 (vf 0.0 0.0) (vf 0.0 3.0)) 3.0 > (dist1 (vf 1.0 1.0) (vf 3.0 3.0)) 4.0 > ;;; Compiler in production mode (report-compiler-options) ;;; Compiler options are: ;;; Target.............SUN-68881 ;;; Egc........................T ;;; Show-Optimizations.......Nil ;;; Undef-Warnings.............T ;;; Warnings...................T ;;; Documentation..............T ;;; Read-Safety..............Nil ;;; Write-Safety.............Nil ;;; Bounds-Check.............Nil ;;; File-Messages..............T ;;; Messages.................Nil ;;; Fast-Entry...............Nil ;;; Tail-Merge.................T ;;; Notinline................Nil ;;; Compiler optimizations are: ;;; Speed......................3 ;;; Safety.....................1 ;;; Space......................0 ;;; Compilation-Speed..........0 > (compile 'dist1) Dist1 > ;;; The starting point of my investigations... ;;; v1 & v2 obtained via my application (dist1 v1 v2) >>Error: A condition of type FLOATING-POINT-UNDERFLOW occurred. DIST1: Required arg 0 (V1): #(0.0 0.0) Required arg 1 (V2): #(0.0 2.1457270408163267) :C 0: Use de-normalized (or zero) result :A 1: Abort to Lisp Top Level -> :c Use de-normalized (or zero) result 3.666E-321 > ;;; Enjoy: same examples as above (dist1 (vf 0.0 0.0) (vf 0.0 3.0)) 0.0 > (dist1 (vf 1.0 1.0) (vf 3.0 3.0)) 3.0 > ;;; The same bound to variables (setq vv1 (vf 0.0 0.0) vv2 (vf 0.0 3.0)) 3.0 > (dist1 vv1 vv2) >>Error: A condition of type FLOATING-POINT-UNDERFLOW occurred. DIST1: Required arg 0 (V1): #(0.0 0.0) Required arg 1 (V2): #(0.0 3.0) :C 0: Use de-normalized (or zero) result :A 1: Abort to Lisp Top Level -> :a Abort to Lisp Top Level Back to Lisp Top Level --------------------------------------------- Disassembling dist1 and other: ;;; Tata works well ;;; The code is intersting because it shows that aref 0 and 1 ;;; are separated by 8 bytes (= 13 - 5) (defun tata (v1 v2 &aux (res 0.0)) (declare (type (simple-array float (*)) v1 v2) (float res)) (incf res (the float (abs (the float (- (the float (aref v1 0)) (the float (aref v2 0))))))) (incf res (the float (abs (the float (- (the float (aref v1 1)) (the float (aref v2 1))))))) res) > (disassemble 'tata) CMPI.W #2, D6 BEQ L1 MOVEA.L (227,A4), A2 ; Sq-Incorrect-No-Args JSR (5,A2) L1: MOVEA.L (-12,A6), A0 MOVEA.L (17,A5), A2 ; 0.0 FMOVE.D (6,A2), FP1 FMOVE.D (5,A0), FP2 MOVEA.L (-16,A6), A1 FMOVE.D (5,A1), FP3 FSUB.X FP3, FP2 FABS.X FP2, FP3 FADD.X FP3, FP1 FMOVE.D (13,A0), FP3 FMOVE.D (13,A1), FP2 FSUB.X FP2, FP3 FABS.X FP3, FP2 FADD.X FP2, FP1 MOVEA.L (907,A4), A2 ; Sq-Cons-Float JSR (5,A2) FMOVE.D FP1, (6,A2) MOVE.L A2, (4,A6) MOVEA.L (-4,A6), A5 LEA (-8,A6), A7 MOVEA.L (A7), A3 JMP (A3) ;;; Dist1 now: ;;; The index for dotimes hasa step of 4, I guess the LSL instruction ;;; is to achieve the 8 bytes seen above ;;; I think the bug is that there should not be applied to the counter ;;; (here D0) but to a copy. (see the === marks) > (disassemble 'dist1) CMPI.W #2, D6 BEQ L1 MOVEA.L (227,A4), A2 ; Sq-Incorrect-No-Args JSR (5,A2) L1: MOVEA.L (-12,A6), A0 MOVEA.L (17,A5), A2 ; 0.0 FMOVE.D (6,A2), FP1 MOVEQ #0, D0 L3: CMPI.L #8, D0 BGE L2 LSL.L #1, D0 === FMOVE.D (5,A0,D0.L), FP2 MOVEA.L (-16,A6), A1 LSL.L #1, D0 === FMOVE.D (5,A1,D0.L), FP3 FSUB.X FP3, FP2 FABS.X FP2, FP3 FADD.X FP3, FP1 ADDQ.L #4, D0 BRA L3 L2: MOVEA.L (907,A4), A2 ; Sq-Cons-Float JSR (5,A2) FMOVE.D FP1, (6,A2) MOVE.L A2, (4,A6) MOVEA.L (-4,A6), A5 LEA (-8,A6), A7 MOVEA.L (A7), A3 JMP (A3) ;;; The final test ;;; It is the same function, with i being put in variables ;;; at the critical point. This version works. > (defun dist1 (v1 v2 &aux (res 0.0)) (declare (type (simple-array double-float (*)) v1 v2) (float res)) (dotimes (i 2) (incf res (the float (abs (the float (- (the float (aref v1 (setq x i))) (the float (aref v2 (setq y i)))))))) ) res) Dist1 > (compile 'dist1) ;;; Warning: Free variable X assumed to be special ;;; Warning: Free variable Y assumed to be special Dist1 ;;; Here, the LSL instruction is found twice too, ;;; but they're not applied to the counter (now D2) ;;; So the preceding code was a real bug... > (disassemble 'dist1) CMPI.W #2, D6 BEQ L1 MOVEA.L (227,A4), A2 ; Sq-Incorrect-No-Args JSR (5,A2) L1: MOVEA.L (-12,A6), A0 MOVEA.L (17,A5), A2 ; 0.0 FMOVE.D (6,A2), FP5 MOVEQ #0, D2 L3: CMPI.L #8, D2 BGE L2 MOVEA.L (21,A5), A1 ; X MOVE.L D2, (3,A1) MOVE.L D2, D1 LSL.L #1, D1 === FMOVE.D (5,A0,D1.L), FP6 FMOVE.X FP6, FP4 LEA (-16,A6), A7 MOVEA.L (25,A5), A1 ; Y MOVE.L D2, (3,A1) MOVE.L D2, D0 MOVEA.L (-16,A6), A1 LSL.L #1, D0 === FMOVE.D (5,A1,D0.L), FP6 FMOVE.X FP6, FP3 LEA (-16,A6), A7 FMOVE.X FP4, FP6 FSUB.X FP3, FP6 FMOVE.X FP6, FP2 LEA (-16,A6), A7 FABS.X FP2, FP6 FMOVE.X FP6, FP1 LEA (-16,A6), A7 FMOVE.X FP5, FP6 FADD.X FP1, FP6 FMOVE.X FP6, FP5 LEA (-16,A6), A7 ADDQ.L #4, D2 BRA L3 L2: MOVEA.L (907,A4), A2 ; Sq-Cons-Float JSR (5,A2) FMOVE.D FP5, (6,A2) MOVE.L A2, (4,A6) MOVEA.L (-4,A6), A5 LEA (-8,A6), A7 MOVEA.L (A7), A3 JMP (A3) Nil ------------------------------------