(include-if (null (get 'chead 'version)) "../chead.l")
"$Header: funb.l,v 1.13 87/12/15 17:02:17 sklower Exp $")
;;; ---- f u n b function compilation
;;; -[Wed Aug 24 17:14:56 1983 by layer]-
;--- c-declare :: handle the "declare" form
; if a declare is seen inside a function definition, we just
; ignore it. We probably should see what it is declareing, as it
; might be declaring a special.
(defun c-declare nil nil)
;--- c-do :: compile a "do" expression
; we note the special case of tst being nil, in which case the loop
; is evaluated only once, and thus acts like a let with labels allowed.
; The do statement is a cross between a prog and a lambda. It is like
; a prog in that labels are allowed. It is like a lambda in that
; we stack the values of all init forms then bind to the variables, just
; like a lambda expression (that is the initial values of even specials
; are stored on the stack, and then copied into the value cell of the
; atom during the binding phase. From then on the stack location is
(let (b-vrbls b-tst b-body chklab bodylab x-repeat x-vrbs x-fst
g-loc g-cc oldreguse (g-decls g-decls))
(forcecomment '(beginning do))
(setq g-loc 'reg chklab (d-genlab) bodylab (d-genlab))
(if (and (cadr v-form) (atom (cadr v-form)))
then (setq v-form (d-olddo-to-newdo (cdr v-form))))
(push (cons 'do 0) g-locs) ; begin our frame
(setq b-vrbls (cadr v-form)
; push value of init forms on stack
(d-pushargs (mapcar '(lambda (x)
then nil ; no init form => nil
; now bind to the variables in the vrbls form
(d-bindlamb (mapcar '(lambda (x)
; search through body for all labels and assign them gensymed labels
(do ((ll b-body (cdr ll))
(if (and (car ll) (symbolp (car ll)))
(cons (car ll) (d-genlab))))))
; if the test is non nil, we do the test
; another strange thing, a test form of (pred) will not return
; the value of pred if it is not nil! it will return nil -- in this
; way, it is not like a cond clause
(if b-tst then (e-label chklab)
(let ((g-cc (cons nil bodylab)) g-loc g-ret)
(d-exp (car b-tst))) ; eval test
then (setq oldreguse (copy g-reguse))
(setq g-reguse oldreguse)
(e-goto (caar g-labs)) ; leave do
(e-label bodylab)) ; begin body
(do ((ll b-body (cdr ll))
(if (or (null (car ll)) (not (symbolp (car ll))))
else (e-label (cdr (assoc (car ll) (cdar g-labs))))
then ; determine all repeat forms which must be
; evaluated, and all the variables affected.
; store the results in x-repeat and x-vrbs
; if there is just one repeat form, we calculate
; its value directly into where it is stored,
; if there is more than one, we stack them
; and then store them back at once.
(do ((ll b-vrbls (cdr ll)))
(if (and (dtpr (car ll)) (cddar ll))
then (Push x-repeat (caddar ll))
(Push x-vrbs (caar ll))))
then (if (null (cdr x-vrbs)) ; if just one repeat
then (let ((g-loc (d-locv (car x-vrbs)))
else (setq x-fst (car x-repeat))
(let ((g-loc (d-locv (car x-vrbs)))
(do ((ll (cdr x-vrbs) (cdr ll)))
(setq g-locs (cdr g-locs))
(e-label (caar g-labs)) ; end of do label
(setq g-labs (cdr g-labs))))
;--- d-olddo-to-newdo :: map old do to new do
; form of old do is (do var tst . body)
; where var is a symbol, not nil
(defun d-olddo-to-newdo (v-l)
`(do ((,(car v-l) ,(cadr v-l) ,(caddr v-l)))
;--- cc-dtpr :: check for dtprness
(d-typesimp (cadr v-form) #.(immed-const 3)))
;--- cc-eq :: compile an "eq" expression
(let ((arg1 (cadr v-form))
(if (setq arg2loc (d-simple arg2))
then (if (setq arg1loc (d-simple arg1))
then ; eq <simple> <simple>
else ; eq <nonsimple> <simple>
(let ((g-loc 'reg) ; put <nonsimple> in reg
; cc->& may have modified
(g-trueop #+(or for-vax for-tahoe) 'jneq
(g-falseop #+(or for-vax for-tahoe) 'jeql
else ; since second is nonsimple, must stack first
(g-trueop #+(or for-vax for-tahoe) 'jneq #+for-68k 'jne)
(g-falseop #+(or for-vax for-tahoe) 'jeql #+for-68k 'jeq)
(setq g-loc 'reg) ; second arg to reg
(setq g-locs (cdr g-locs))
;--- cc-equal :: compile `equal'
(d-pushargs (cdr v-form))
(e-cmp '(-8 #.np-reg) '(-4 #.np-reg))
(d-calltran 'equal '2) ; not eq, try equal.
#+(or for-vax for-tahoe) (e-tst (e-cvt 'reg))
#+for-68k (e-cmpnil (e-cvt 'reg))
(if g-loc then (d-move 'Nil g-loc))
(if (cdr g-cc) then (e-goto (cdr g-cc))
else (e-goto (setq lab2 (d-genlab))))
(if g-loc then (d-move 'T g-loc))
(if (car g-cc) then (e-goto (car g-cc)))
(if lab2 then (e-writel lab2))
(setq g-locs (cddr g-locs))
(setq g-loccnt (- g-loccnt 2))))
;--- c-errset :: compile an errset expression
; the errset has this form: (errset 'value ['tag])
; where tag defaults to t.
(d-exp (if (cddr v-form) then (caddr v-form) else t))
(d-pushframe #.F_CATCH (d-loclit 'ER%all nil) 'reg)
(push nil g-labs) ; disallow labels
; If retval is non zero then an error has throw us here so we
; must recover the value thrown (from _lispretval) and leave
; If retval is zero then we shoud calculate the expression
; into r0 and put a cons cell around it
(e-write2 #+(or for-vax for-tahoe) 'jeql #+for-68k 'jeq beglab)
(e-move '_lispretval (e-cvt 'reg))
(e-write2 #+(or for-vax for-tahoe) 'jbr #+for-68k 'jra finlab)
(d-move 'Nil 'stack) ; haven't updated g-loc, g-loccnt but it
; shouldn't hurt (famous last words)
(unpush g-locs) ; remove (catcherrset . 0)
(unpush g-labs) ; remove nil
;--- cm-fixnum-cxr :: open code a fixnum-cxr expression.
; fixnum-cxr is a compile only hacky function which accesses an element
; of a fixnum space and boxes the resulting fixnum. It can be used
; for rapid access to user defined structures.
`(internal-fixnum-box (cxr ,@(cdr v-form))))
(defun c-internal-fixnum-box ()
#+for-68k (d-regused '#.fixnum-reg)
; return a pointer to the address of the object instead of the object.
;--- cc-fixp :: check for a fixnum or bignum
(d-typecmplx (cadr v-form)
'#.(immed-const (plus 1_2 1_9))))
;--- cc-floatp :: check for a flonum
(d-typesimp (cadr v-form) #.(immed-const 4)))
;--- c-funcall :: compile a funcall
; we open code a funcall the resulting object is a compiled lambda.
; We don't open code nlambda and macro funcalls since they are
; rarely used and it would waste space to check for them
then (comp-err "funcall requires at least one argument " v-form))
(args (length (cdr v-form)))
(d-pushargs (cdr v-form))
(rplaca (nthcdr (1- args) g-locs) 'funcallfcn)
(d-exp '(cond ((and (symbolp funcallfcn)
(setq funcallfcn (getd funcallfcn)))))
(d-exp `(cond ((and (bcdp funcallfcn) (eq 'lambda (getdisc funcallfcn)))
(Internal-bcdcall ,args t))
(t (Internal-bcdcall ,args nil))))))
; this is a compiler internal function call. when this occurs, there
; are argnum objects stacked, the first of which is a function name
; or bcd object. If dobcdcall is t then we want to do a bcdcall of
; the first object stacked. If it is not true then we want to
; call the interpreter funcall function to handle it.
(defun c-Internal-bcdcall nil
(let ((argnum (cadr v-form))
(dobcdcall (caddr v-form)))
(cond (dobcdcall (d-bcdcall argnum))
(t (d-calltran 'funcall argnum)))))
;--- cc-function :: compile a function function
; function is an nlambda, which the interpreter treats as 'quote'
; If the argument is a lambda expression, then Liszt will generate
; a new function and generate code to return the name of
; that function. If the argument is a symbol, then 'symbol
; is compiled. It would probably be better to return the function
; cell of the symbol, but Maclisp returns the symbol and it
; would cause compatibility problems.
(if (or (null (cdr v-form))
then (comp-err "Wrong number of arguments to 'function': " v-form))
(let ((arg (cadr v-form)))
(memq (car arg) '(lambda nlambda lexpr)))
then (let ((newname (concat "in-line-lambda:"
(setq in-line-lambda-number
(add1 in-line-lambda-number)))))
(Push liszt-process-forms
else (comp-err "Illegal argument to 'function': " v-form))))
;--- c-get :: do a get from the prop list
(if (not (eq 2 (length (cdr v-form))))
then (comp-err "Wrong number of args to get " v-form))
(d-pushargs (cdr v-form)) ; there better be 2 args
(setq g-locs (cddr g-locs))
(setq g-loccnt (- g-loccnt 2)))
;--- cm-getaccess :: compile a getaccess instruction
(defun cm-getaccess nil `(cdr ,(cadr v-form)))
;--- cm-getaux :: compile a getaux instruction
(defun cm-getaux nil `(car ,(cadr v-form)))
;--- cm-getd :: compile a getd instruction
; the getd function is open coded to look in the third part of a symbol
(defun cm-getd nil `(cxr 2 ,(cadr v-form)))
;--- cm-getdata :: compile a getdata instruction
; the getdata function is open coded to look in the third part of an
(defun cm-getdata nil `(cxr 2 ,(cadr v-form)))
;--- cm-getdisc :: compile a getdisc expression
; getdisc accessed the discipline field of a binary object.
(defun cm-getdisc nil `(cxr 1 ,(cadr v-form)))
;--- c-go :: compile a "go" expression
; we only compile the (go symbol)type expression, we do not
; allow symbol to be anything by a non null symbol.
; find number of frames we have to go down to get to the label
(do ((labs g-labs (cdr labs))
(comp-err "go label not found for expression: " (or v-form)))
(if (car labs) ; if we have a set of labels to look at...
(do ((lbs (cdar labs) (cdr lbs)))
(if (eq (caar lbs) (cadr v-form))
then (return (cdar lbs)))))
then (if (not (eq labs g-labs))
then (comp-note g-fname ": non local go used : "
; three stack to pop: namestack, bindstack
(if (greaterp specials 0)
then (e-unshallowbind specials))
(if (greaterp catcherrset 0)
": Go through a catch or errset "
; tally all locals, specials and catcherrsets used in this frame
(if (eq 'catcherrset (caar locs))
elseif (eq 'progv (caar locs))
then (comp-err "Attempt to 'go' through a progv"))
(setq specials (+ specials (cdar locs))
;--- cc-ignore :: just ignore this code
;--- c-lambexp :: compile a lambda expression
(let ((g-loc (if (or g-loc g-cc) then 'reg))
(g-locs (cons (cons 'lambda 0) g-locs))
(g-labs (cons nil g-labs)))
(d-pushargs (cdr v-form)) ; then push vals
(d-lambbody (car v-form))
;--- d-lambbody :: do a lambda body
; - body : body of lambda expression, eg (lambda () dld)
(d-scanfordecls (cddr body)) ; look for declarations
(d-bindlamb (cadr body)) ; bind locals
(d-exp (do ((ll (cddr body) (cdr ll))
((null (cdr ll)) (car ll))
(d-unbind))) ; unbind this frame
;--- d-bindlamb :: bind variables in lambda list
; - vrbs : list of lambda variables, may include nil meaning ignore
(let ((res (d-bindlrec (reverse vrbs) g-locs 0 g-loccnt)))
(if res then (e-setupbind)
(mapc '(lambda (vrb) (e-shallowbind (car vrb) (cdr vrb)))
;--- d-bindlrec :: recusive routine to bind lambda variables
; - vrb : list of variables yet to bind
; - locs : current location in g-loc
; - specs : number of specials seen so far
; - lev : how far up from the bottom of stack we are.
; returns: list of elements, one for each special, of this form:
; (<specialvrbname> stack <n>)
; where specialvrbname is the name of the special variable, and n is
; the distance from the top of the stack where its initial value is
; also: puts the names of the local variables in the g-locs list, as well
; as placing the number of special variables in the lambda header.
(defun d-bindlrec (vrb locs specs lev)
then (let ((spcflg (d-specialp (car vrb)))
(if spcflg then (setq specs (1+ specs)))
(if (cdr vrb) ; if more vrbls to go ...
then (setq retv (d-bindlrec (cdr vrb)
specs)) ; else fix up lambda hdr
(if (not spcflg) then (rplaca locs (car vrb))
else (Push retv `(,(car vrb) stack ,lev)))
; forms - the body of a lambda, prog or do.
; we look down the form for 'declare' forms. They should be at the
; beginning, but there are macros which may unintentionally put forms
; in front of user written forms. Thus we check a little further than
(defun d-scanfordecls (forms)
; look for declarations in the first few forms
(do ((count 3 (1- count)))
(cond ((and (dtpr (car forms))
(eq 'declare (caar forms))
(apply 'liszt-declare (cdar forms)))))
(setq forms (cdr forms))))
;--- c-list :: compile a list expression
; this is compiled as a bunch of conses with a nil pushed on the
(setq nargs (length (cdr v-form)))
(makecomment '(list expression))
then (d-move 'Nil 'reg) ; (list) ==> nil
(d-pushargs (cdr v-form))
#+(or for-vax for-tahoe) (e-write2 'clrl '#.np-plus) ; stack one nil
#+for-68k (L-push (e-cvt 'Nil))
(do ((i (max 1 nargs) (1- i)))
(if (> i 1) then (L-push (e-cvt 'reg))))
(setq g-locs (nthcdr nargs g-locs)
g-loccnt (- g-loccnt nargs))))
;--- d-mapconvert - access : function to access parts of lists
; - join : function to join results
; - resu : function to apply to result
; This function converts maps to an equivalent do form.
; in this function, the variable vrbls contains a list of forms, one form
; per list we are mapping over. The form of the form is
; (dummyvariable realarg (cdr dummyvariable))
; realarg may be surrounded by (setq <variable which holds result> realarg)
; in the case that the result is the list to be mapped over (this only occurs
; with the function mapc).
(defun d-mapconvert (access join resu form )
(prog (vrbls finvar acc accform compform
tmp testform tempvar lastvar)
(setq finvar (gensym 'X) ; holds result
(cond ((or resu (cdr arg))
; the access form will either be nil or car. If it is
; nil, then we are doing something like a maplist,
; if the access form is car, then we are doing something
acc (mapcar '(lambda (tem)
(cond (access `(,access ,(car tem)))
accform (cond ((or (atom (setq tmp (car form)))
(null (setq tmp (d-macroexpand tmp)))
(not (member (car tmp) '(quote function))))
(t `(,(cadr tmp) ,@acc)))
; the testform checks if any of the lists we are mapping
; over is nil, in which case we quit.
testform (cond ((null (cdr vrbls)) `(null ,(caar vrbls)))
(t `(or ,@(mapcar '(lambda (x)
; in the case of mapcans and mapcons, you need two
; extra variables to simulate the nconc.
; testvar gets intermediate results and lastvar
; points to then end of the list
then (setq tempvar (gensym 'X)
vrbls `((,tempvar) (,lastvar) ,@vrbls)))
`(cond ((setq ,tempvar ,accform)
(rplacd ,lastvar ,tempvar))
(join `(setq ,finvar (,join ,accform ,finvar)))
,(cond ((eq resu 'identity) finvar)
; apply to successive elements, return second arg
(d-mapconvert 'car nil nil (cdr v-form)))
; apply to successive elements, return list of results
(d-mapconvert 'car 'cons 'nreverse (cdr v-form)))
; apply to successive elements, returned nconc of results
(d-mapconvert 'car 'nconc 'identity (cdr v-form)))
; apply to successive sublists, return second arg
(d-mapconvert nil nil nil (cdr v-form)))
; apply to successive sublists, return list of results
(d-mapconvert nil 'cons 'reverse (cdr v-form)))
; apply to successive sublists, return nconc of results
(d-mapconvert nil 'nconc 'identity (cdr v-form)))
;--- cc-memq :: compile a memq expression
(let ((loc1 (d-simple (cadr v-form)))
(loc2 (d-simple (caddr v-form)))
else (let ((g-loc 'stack)
; now set up the jump addresses
then (setq loc1 (if (car g-cc) thenret else (d-genlab))
loc2 (if (cdr g-cc) thenret else (d-genlab)))
else (setq loc1 (d-genlab)
(setq looploc (d-genlab))
then (e-label loc2) ; nil result
else (e-goto (setq finlab (d-genlab))))
then (e-label loc1) ; non nil result
(if (car g-cc) then (e-goto (car g-cc)))
else (if (null (car g-cc)) then (e-label loc1)))
(if finlab then (e-label finlab))))
(let ((loc1 (d-simple (cadr v-form)))
(loc2 (d-simple (caddr v-form)))
(tmp-data-reg (d-alloc-register 'd nil)))
(d-clearreg tmp-data-reg)
then (d-move loc1 tmp-data-reg)
else (let ((g-loc tmp-data-reg)
else (let ((g-loc 'stack)
; now set up the jump addresses
then (setq loc1 (if (car g-cc) thenret else (d-genlab))
loc2 (if (cdr g-cc) thenret else (d-genlab)))
else (setq loc1 (d-genlab)
(setq looploc (d-genlab))
(e-cmp tmp-data-reg '(4 a0))
then (e-label loc2) ; nil result
else (e-goto (setq finlab (d-genlab))))
then (e-label loc1) ; non nil result
(d-move 'a0 g-loc) ;a0 was cdr of non-nil result
(if (car g-cc) then (e-goto (car g-cc)))
else (if (null (car g-cc)) then (e-label loc1)))
(if finlab then (e-label finlab))))