Skip to content

Unusual control flow rarely screws up contify #13

Description

@Bike

;; If the return-point has a predecessor, it does not start a
;; block and will be the unique outside call to this
;; function, which means we should normalize the return point
;; to be a dummy block.
(unique-call
(and (not (eq return-point :unknown))
(cleavir-bir:predecessor return-point)))
(return-point
(if unique-call
(progn
(check-type unique-call cleavir-bir:local-call)
(let ((dummy-block (nth-value 1 (cleavir-bir:split-block-after unique-call)))
(ucall-out (cleavir-bir:output unique-call)))
(unless (cleavir-bir:unused-p ucall-out)
(let ((phi (make-instance 'cleavir-bir:phi :iblock dummy-block)))
(setf (cleavir-bir:inputs dummy-block) (list phi))
;; Replace the call-as-datum with the return-values.
(cleavir-bir:replace-uses phi ucall-out)))
dummy-block))
(if (eq return-point :unknown)
:unknown
(cleavir-bir:iblock return-point))))

Here, contify only recognizes two situations. Either there are multiple calls, and the results of the call (if used) are fed into a shared phi; or, there is only one call, and the return point is the successor of the call. If the return point is the beginning of an iblock, it assumes that it is in the former situation. In this situation, in order to communicate the return values of the inlined function, that iblock's phi, assumed to be the shared results, is used.

However, it is possible to contrive things so that the return point is the beginning of an iblock even if there is only one call, and in a way such that the results of the call are actually needed. Here is the smallest example I could come up with, reduced from clasp-developers/clasp#1181:

(lambda (reqs opts)                                                                                                            
  (values                                                                                                         
   (BLOCK NIL
     (LET* ((LOOP-LIST-HEAD27101 (LIST NIL))
            (LOOP-LIST-TAIL27102 LOOP-LIST-HEAD27101))
       (TAGBODY
        NEXT-LOOP
          (return-from nil (cdr loop-list-head27101))
          (GO NEXT-LOOP))))

   ;; CHECK-TYPE optional parameters                                                                                                                               
   (TAGBODY
      (GO G27058)
    G27058
      (when opts (GO G27058)))))

For this code, the results of the call to cdr (which is inlined) are used in the final return value. However, the immediate successor of the call, after catch reduction, is a jump to G27058, and the return-point is then the first instruction in that iblock. This "tricks" the contifier into thinking it does not need to create a new iblock/phi. Then when it tries to delete the call, it finds that the call is still used, and the assertion in the delete function ((EVERY CLEAVIR-BIR:UNUSED-P (CLEAVIR-BIR:OUTPUTS CLEAVIR-BIR:INSTRUCTION))) fails.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions