[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: GO in T?



    From: Mike Caplinger <mike@RICE.ARPA>

    While I shudder to ask this question, is there some way to simulate a
    general GO in T?  CATCH requires you to know, a priori, about the
    control flow globally in the program, making automatic translation
    difficult.

    I have two applications in mind; translation from some other Lisp
    dialect into T, and using T as an intermediate form for Algol-like
    languages.

Good question.  I admit the need is real.  I give you 4 answers.

#1. If you were writing in pure Scheme with upwards escape functions,
you could translate

  (prog (n a)
     y  (set n 5)
        (set a 1)
     z  (cond ((= n 0) (go e)))
        (set a (* a n))
        (set n (- n 1))
        (go z)
     e  (return a))

as

  (let ((n nil) (a nil))
    ((catch go
       (catch return
         (labels (((y)
                   (set n 5)
                   (set a 1)
                   (go z))
                  ((z)
                   (cond ((= n 0) (go e)))
                   (set a (* a n))
                   (set n (- n 1))
                   (go z))
                  ((e)
                   (return a)))
            (y))))))
         
and it would work quite transparently.  Unfortunately, bad things
happen when you run this in T.

#2. If you can make sure that all your GO's have the right
tail-recursive tail-recursive properties, you can use LABELS directly,
and get good object code (direct jumps, no closure consing).  However,
I think this is the problem you were alluding to, so it's not directly
acceptable.

  (let ((n nil) (a nil) (go (lambda (tag) (tag))))
    (catch return
      (labels (((y)
                (set n 5)
                (set a 1)
                (go z))
               ((z)
                (cond ((= n 0) (go e))
                      (else             ;note nontrivial change
                       (set a (* a n))
                       (set n (- n 1))
                       (go z))))
               ((e)
                (return a)))
        (y))))

#3. If you can't make the guarantee of #2, you can use a driver loop,
and a CATCH for each segment of the PROG:

  (let ((n nil) (a nil))
    (catch return
      (labels (((y)
                (catch go
                  (set n 5)
                  (set a 1)
                  (go z)))
               ((z)
                (catch go
                  (cond ((= n 0) (go e)))
                  (set a (* a n))
                  (set n (- n 1))
                  (go z)))
               ((e)
                (catch go
                  (return a))))
        (do ((tag y (tag))) (nil)))))

(The current TC is so buggy with respect to CATCH/LABELS interactions
that I wouldn't expect this to compile correctly.)

#4. If you want more efficient compilation using current compiler
technology, you can throw small integers instead of closures to
the driver loop:

  (let ((n nil) (a nil))
    (catch return
      (let ((y 0)
            (z 1) ;assign unique small numbers to all tags for fast dispatch
            (e 2))
        (do ((tag y (xselect tag
                      ((y)
                       (catch go
                         (set n 5)
                         (set a 1)
                         (go z)))
                      ((z)
                       (catch go
                         (cond ((= n 0) (go e)))
                         (set a (* a n))
                         (set n (- n 1))
                         (go z)))
                      ((e)
                       (catch go
                         (return a))))))
            (nil))))

One could imagine a compiler which "did the right thing" with this
(compiled direct jumps instead of jumps to a dispatch), but of course
there's not much motivation to write one (however, either this or
a primitive GO would be important if we were serious about compiling
Common Lisp or Fortran).

A higher-tech solution would be to do some code analysis to translate
most PROG code into code which would fit solution #2.  This was done
in an early SCHEME interpreter (NSCHSY; circa 1977-78) at MIT.  I
think Jim Meehan at Cognitive Systems (Meehan@Yale), and perhaps some
Yale people (McDermott? Riesbeck?) have also worked on this problem;
maybe one of them could comment.