Comment 1 for bug 1446891

Douglas Katzman (dougk) wrote :

A slight reduction of the above. Call it with (PREPARE #(3))

(defun prepare (context)
  (declare (optimize (debug 1) (safety 0)))
  (let ((npcs (the fixnum (svref context 0)))
        (outer-index 3))
    (tagbody
      outer-loop
      (let ((payload
             (svref #(#((a . b) (c . d) (e . f))
                              (g . h)
                              (o . p)
                              #((i . j) (k . l) (m . n)))
                            outer-index)))
        (loop for jj fixnum from 0 below (if (listp payload) 1 npcs)
            do (print jj)
               (when (eq payload nil) ; always false
                 ;; the mere presence of this unwind-protect borks things
                 (unwind-protect t))))
       (unless (minusp (decf outer-index)) (go outer-loop)))))

You can see the problem from this fragment of the trace file.
lvar 30 holds the [incorrect] result of (IF (LISTP PAYLOAD) 1 NPCS).
But lvar 30 has only a single WRITE into it. The write for the ELSE branch of the IF is missing, and the ELSE code jumps directly to the top of the loop.
Also note that the lambda-let for (loop for jj ...) elides the binding of #:loop-limit-0 and reads directly from lvar 30, which would have been OK if lvar 30 were correct.

IR1 block 5 start c52
start stack:
 52> entry NIL
 53> 54: SB-C::CLAMBDA (LET ((JJ 0)
                                                            (#:LOOP-LIMIT-0 (IF (LISTP PAYLOAD) 1 NPCS))))
 55> 56: '0
 57> 58: LISTP {GLOBAL-FUNCTION}
 59> 60: PAYLOAD
 61> 62: known combination v58 v60
 63> if v62 c64 c65
end stack:
successors c65 c64

IR1 block 19 start c64
start stack:
 64> 30: '1
end stack:
successors c65

IR1 block 6 start c65
start stack:
 65> local combination v54 v56 <none>
 66> bind SB-C::CLAMBDA (LET ((JJ 0)
                                  (#:LOOP-LIMIT-0 (IF (LISTP PAYLOAD) 1 NPCS)))
                              ) :KIND :LET
end stack:
successors c67

IR1 block 7 start c67
start stack:
 67> entry NIL
end stack:
successors c68

IR1 block 8 start c68
start stack:
 68> 69: JJ
 70> 71: < {GLOBAL-FUNCTION}
 72> 73: known combination v71 v69 v30 ; <<< BUG. v30 was only written by one branch of the IF.
 74> if v73 c75 c76
end stack:
successors c76 c75

---
For comparison's sake, here is the code when you artificially inhibit single-use lvar elimination with (DEBUG 2) or whatever.
Lvar 66 holds the result of (IF (LISTP PAYLOAD) 1 NPCS). It is written from both successor blocks of the IF, as expected.
The comparison of JJ is against the lambda var #:LOOP-LIMIT-0. Though I think it would have been legal to elide the lambda var and read from lvar 66 which was correctly computed.

IR1 block 5 start c52
start stack:
 52> entry NIL
 53> 54: SB-C::CLAMBDA (LET ((JJ 0)
                             (#:LOOP-LIMIT-0 (IF (LISTP PAYLOAD) 1 NPCS))))
 55> 56: '0
 57> 58: LISTP {GLOBAL-FUNCTION}
 59> 60: PAYLOAD
 61> 62: known combination v58 v60
 63> if v62 c64 c65
end stack:
successors c65 c64

IR1 block 20 start c64
start stack:
 64> 66: '1
end stack:
successors c67

IR1 block 6 start c65
start stack:
 65> 66: NPCS
end stack:
successors c67

IR1 block 7 start c67
start stack:
 67> local combination v54 v56 v66
 68> bind SB-C::CLAMBDA (LET ((JJ 0)
                                  (#:LOOP-LIMIT-0
                                   (IF (LISTP PAYLOAD) 1 NPCS)))
                              ) :KIND :LET
end stack:
successors c69

IR1 block 8 start c69
start stack:
 69> entry NIL
end stack:
successors c70

IR1 block 9 start c70
start stack:
 70> 71: JJ
 72> 73: < {GLOBAL-FUNCTION}
 74> 75: #:LOOP-LIMIT-0
 76> 77: known combination v73 v71 v75
 78> if v77 c79 c80
end stack: