;;; Code generator for $call and $lap expressions.
;;;

;*              Copyright 1989 Digital Equipment Corporation
;*                         All Rights Reserved
;*
;* Permission to use, copy, and modify this software and its documentation is
;* hereby granted only under the following terms and conditions.  Both the
;* above copyright notice and this permission notice must appear in all copies
;* of the software, derivative works or modified versions, and any portions
;* thereof, and both notices must appear in supporting documentation.
;*
;* Users of this software agree to the terms and conditions set forth herein,
;* and hereby grant back to Digital a non-exclusive, unrestricted, royalty-free
;* right and license under any changes, enhancements or extensions made to the
;* core functions of the software, including but not limited to those affording
;* compatibility with other hardware or software environments, but excluding
;* applications which incorporate this software.  Users further agree to use
;* their best efforts to return to Digital any such changes, enhancements or
;* extensions that they make and inform Digital of noteworthy uses of this
;* software.  Correspondence should be provided to Digital at:
;* 
;*                       Director of Licensing
;*                       Western Research Laboratory
;*                       Digital Equipment Corporation
;*                       100 Hamilton Avenue
;*                       Palo Alto, California  94301  
;* 
;* This software may be distributed (but not offered for sale or transferred
;* for compensation) to third parties, provided such third parties agree to
;* abide by the terms and conditions of this notice.  
;* 
;* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
;* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF
;* MERCHANTABILITY AND FITNESS.   IN NO EVENT SHALL DIGITAL EQUIPMENT
;* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
;* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
;* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
;* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
;* SOFTWARE.

(module callcode)

;;; External and in-line declarations.

(include "plist.sch")
(include "expform.sch")
(include "lambdaexp.sch")
(include "miscexp.sch")
(include "gencode.sch")
(include "lap.sch")

;;; ($call tail func arg ...)  ==>  emit code for the call and return it's
;;;				    result.
;;;
;;; The first step in generating code for a call is figuring out the type of
;;; function being called and the type of call.  This is done by the following
;;; function which then calls the appropriate function to actually generate
;;; code for the call.

(define ($CALL-GENC loc exp bindings)
    (let* ((func ($call-func exp))
	   (tail ($call-tail exp))
	   (argl ($call-argl exp))
	   (id   (if (symbol? func) (id-lambda func) #f)))
	  (cond (($lambda? func)
		 (inline-call loc func argl bindings))
		(($lap? func)
		 ($lap-genc loc func argl bindings))
		((and id tail (or (eq? (lambda-generate id) 'inline-tail)
				  (la-exits-lb? tail id)))
		 (tail-call loc func argl id bindings))
		((and id (eq? (lambda-generate id) 'inline))
		 (inline-call loc (lambda-$lambda id) argl bindings))
		((and id (id-type func))
		 (known-c-call loc func argl id bindings))
		(id
		 (known-call loc func argl id bindings))
		(else
		 (unknown-call loc func argl bindings)))))

(define (LA-EXITS-LB? ida idb)
    (and ida (or (eq? ida idb) (la-exits-lb? (lambda-exits ida) idb))))
	   
;;; When the function is a lambda expression, or when there is only one actual
;;; call to an internally defined function, then it may be expanded in-line.
;;; The lambda expression is effectively treated as LET, with the arguments
;;; used as the initial values.

(define (INLINE-CALL loc exp argl bindings)
    (let* ((id   ($lambda-id exp))
	   (req  (lambda-reqvars id))
	   (opt  (optional-args id))
	   (temp-state (save-lap-temps))
	   (varl '())
	   (vals '())
	   (save-free-display free-display)

	   ;;; Assign a temp. to the lexically bound var if it does not
	   ;;; have a memory location.  Build the varl and vals lists.

	   (bind (lambda (var)
			 (set! varl (cons var varl))
			 (set! vals
			       (cons (cond ((id-display var)
					    (when (not (memq var bindings))
						  (reserve-display (list var)
						      bindings)
						  (set! bindings
							(cons var bindings)))
					    `("DISPLAY" ,(id-display var)))
					   ((not (memq var bindings))
					    (set! bindings (cons var bindings))
					    (set-id-vname! var (use-lap-temp))
					    var)
					   (else var))
				     vals))
			 (car vals))))

	  (cond ((or (and opt (< (length argl) (length req)))
		     (and (null? opt) (not (eq? (length argl) (length req)))))
		 (report-error "Incorrect number of arguments for lambda"))
		(else
		 (for-each
		     (lambda (var)
			     (if (and (id-lambda var)
				      (not (eq? (lambda-generate
						    (id-lambda var))
						'closed-procedure)))
				 (exp-genc 'no-value (car argl) bindings)
				 (exp-genc (bind var) (car argl) bindings))
			     (set! argl (cdr argl)))
		     req)
		 (if opt
		     (listify-optional-args (bind opt) argl bindings))
		 (lambda-body-genc loc exp varl vals bindings)))
	  (set! free-display save-free-display)
	  (restore-lap-temps temp-state)))

;;; Optional arguments are combined into a list by the caller.  This function
;;; emits the code to evaluate each argument and form them into a list.

(define (LISTIFY-OPTIONAL-ARGS var argl bindings)
    (if argl
	(let ((ltemp (use-lap-temp)))
	     (let loop ((argl (reverse argl)) (reg "EMPTYLIST"))
		  (exp-genc 'tos (car argl) bindings)
		  (cond ((cdr argl)
			 (emit-lap `(SET ,(vname ltemp) (CONS tos ,reg)))
			 (loop (cdr argl) (vname ltemp)))
			(else (emit-lap `(SET ,(vname var) (CONS tos ,reg))))))
	     (drop-lap-temp ltemp))
	(emit-lap `(SET ,(vname var) EMPTYLIST))))

;;; When a tail-recursive call can be generated, the following routine is
;;; called.  Tail-recursion elimination is an example of how computer
;;; scientists can gain insight by observing nature.  When a cat tires of
;;; chasing its tail, does it have to "unwind" itself?
 
(define (TAIL-CALL loc func argl id bindings)
    (let ((req  (lambda-reqvars id))
	  (opt  (optional-args id)))
	 (if (or (and opt (< (length argl) (length req)))
		 (and (null? opt) (not (eq? (length argl) (length req)))))
	     (report-error "Incorrect number of arguments for" func)
	     (let ((temp-state (save-lap-temps)))
		  (tail-call-bind req opt argl bindings)
		  (emit-lap `(GOTO ,(code-label id)))
		  (update-condition-info id)
		  (restore-lap-temps temp-state)))))

;;; The arguments to a tail-called function are evaluated and assigned taking
;;; care to avoid the use of a temporary for the evaluation of the last one.
;;; Note the special case where a temporary must be allocated when the value
;;; of the argument is one of the function's variables that is being rebound.
;;; Also note the special handling for binding a variable which is set!.

(define (TAIL-CALL-BIND req opt argl bindings)
    (cond ((and (null? req) (null? opt)))
	  ((null? req)
	   (listify-optional-args (lookup opt bindings) argl bindings))
	  ((and (null? (cdr req)) (null? opt))
	   (let ((var (car req)))
		(cond ((id-set! var)
		       (exp-genc 'tos (car argl) bindings)
		       (emit-lap `(SET ,(let ((x (id-display var)))
					     (if x
						 `("DISPLAY" ,x)
						 (vname var)))
				       tos)))
		      (else
			  (exp-genc (lookup (car req) bindings) (car argl)
			      bindings)))))
	  (else
	   (let ((var (car req))
		 (val (let ((arg (car argl)))
			   (if (or (eq? arg opt) (memq arg (cdr req)))
			       (let ((temp (use-lap-temp)))
				    (exp-genc temp arg bindings)
				    temp)
			       (car (load-argl (list arg) bindings))))))
		(tail-call-bind (cdr req) opt (cdr argl) bindings)
		(let ((displayx (id-display var)))
		     (if (id-set! var)
			 (emit-lap `(SET ,(if displayx
					      `("DISPLAY" ,displayx)
					      (vname var))
					 ,val))
			 (emit-lap `(SET ,(lookup var bindings) ,val))))))))

;;; When the function is a block of lap code, then it will be evaluated here.
;;; Arguments will be looked up and then the lap code will be emitted with
;;; appropriate substitutions.

(define ($LAP-GENC loc lap argl bindings)
    (let ((alist '())
	  (save-temp (save-lap-temps)))
	 (do ((vars ($lap-vars lap) 	      (cdr vars))
	      (vals (load-argl argl bindings) (cdr vals)))
	     ((or (null? vars) (null? vals))
	      (if (or vals vars)
		  (report-error
		      "Incorrect number of arguments for LAP construct")))
	     (set! alist (cons (list (car vars) (car vals)) alist)))
	 (let loop ((laps ($lap-body lap)))
	      (cond ((cdr laps)
		     (emit-lap (subsym (car laps) alist))
		     (loop (cdr laps)))
		    (else (emit-lap `(SET ,(vname loc)
					  ,(subsym (car laps) alist))))))
	 (restore-lap-temps save-temp)))

;;; Arguments are substituted into the lap code by the following function.

(define (SUBSYM exp alist)
    (cond ((null? exp) exp)
	  ((symbol? exp)
	   (let ((old-new (assq exp alist)))
		(if old-new (vname (cadr old-new)) exp)))
	  ((pair? exp)
	   (cons (subsym (car exp) alist)
		 (subsym (cdr exp) alist)))
	  (else exp)))

;;; When a known C function is called, the following procedure emits the code
;;; to call it with converted arguments and then convert the result.

(define (KNOWN-C-CALL loc func argl id bindings)
    (let* ((req       (lambda-reqvars id))
	   (opt       (lambda-optvars id))
	   (reqlen    (length req))
	   (save-lap  (save-lap-temps))
	   (argl      (load-argl argl bindings)))
	  (emit-extern func)
	  (cond ((or (and (null? opt) (not (= (length argl) reqlen)))
		     (and opt (< (length argl) reqlen)))
		 (report-error "Incorrect number of arguments for"
		     (id-printname func)))
		(else
		 (emit-lap
		     `(SET TOS
			   (,(cname id)
			    ,@(let loop ((args argl) (types (append req opt)))
				   (if args
				       (cons 	 	 
					   (case (car types)
					   	 ((char)
						  `(TSCP_CHAR ,(car args)))
						 ((int)
						  `(TSCP_INT ,(car args)))
						 ((shortint)
						  `(SHORTINT
						       (TSCP_INT ,(car args))))
						 ((longint)
						  `(LONGINT
						       (TSCP_INT ,(car args))))
						 ((unsigned)
						  `(TSCP_UNSIGNED ,(car args)))
						 ((shortunsigned)
						  `(SHORTUNSIGNED
						       (TSCP_UNSIGNED
							   ,(car args))))
						 ((longunsigned)
						  `(LONGUNSIGNED
						       (TSCP_UNSIGNED
							   ,(car args))))
						 ((pointer)
						  `(TSCP_POINTER ,(car args)))
						 ((float)
						  `(CFLOAT (TSCP_DOUBLE
							       ,(car args))))
						 ((double)
						  `(TSCP_DOUBLE ,(car args)))
						 (else (car args)))
					   (loop (cdr args)
						 (or (cdr types) types)))
				       '())))))
		 (emit-lap
		     `(SET ,(vname loc)
			   ,(case (or (eq? loc 'no-value) (id-type func))
				  ((#t) 'TOS)
				  ((char) '(CHAR_TSCP TOS))
				  ((int) '(INT_TSCP TOS))
				  ((shortint longint) '(INT_TSCP (INT TOS)))
				  ((unsigned) '(UNSIGNED_TSCP TOS))
				  ((shortunsigned longunsigned)
				   '(UNSIGNED_TSCP (UNSIGNED TOS)))
				  ((pointer) '(POINTER_TSCP TOS))
				  ((float) '(DOUBLE_TSCP (CDOUBLE TOS)))
				  ((double) '(DOUBLE_TSCP TOS))
				  ((void)
				   (report-warning
				       "C procedure does not return a value:"
				       (cname id))
				   (emit-lap '(SET NO-VALUE TOS))
				   'FALSEVALUE)
				  (else '(_TSCP TOS)))))))
	  (restore-lap-temps save-lap)))

;;; When a known function is called, the minimal calling sequence necessary
;;; is generated, and the argument count can be checked at compile time.  Note
;;; the special case for functions with a variable number of arguments which
;;; do not have a module name.  This is to allow calls to ULTRIX or C-library
;;; routines which take a variable number of arguments.

(define (KNOWN-CALL loc func argl id bindings)
    (let* ((req       (lambda-reqvars id))
	   (opt       (optional-args id))
	   (reqlen    (length req))
	   (save-lap  (save-lap-temps)))
	  (emit-extern func)
	  (cond ((or (and (null? opt) (not (= (length argl) reqlen)))
		     (and opt (< (length argl) reqlen)))
		 (report-error "Incorrect number of arguments for"
		     (id-printname func)))
		((and opt (equal? (id-module func) ""))
		 (set! argl (load-argl argl bindings))
		 (emit-lap `(SET ,(vname loc) (,(cname id) ,@argl))))
		(else
		 (set! req (load-argl (list-head argl reqlen) bindings))
		 (if opt
		     (listify-optional-args 'tos (list-tail argl reqlen)
			 bindings))    
		 (emit-lap
		     `(SET ,(vname loc)
			   (,(cname id)
			    ,@req
			    ,@(if opt '(tos) '())
			    ,@(if (eq? (lambda-generate id) 'closed-procedure)
				  `((PROCEDURE_CLOSURE
					,(lookup func bindings)))
				  '()))))))
	  (restore-lap-temps save-lap)))

;;; The most general calling sequence is when nothing is known about the
;;; procedure.  If the procedure takes a fixed number of arguments, then the
;;; call will be in-line, otherwise, a special form of APPLY will be used
;;; as the trampoline.

(define (UNKNOWN-CALL loc func argl bindings)
    (let* ((save-state (save-lap-temps))
	   (proc (use-lap-temp))
	   (argtemps (load-argl argl bindings)))
	  (exp-genc proc func bindings)
	  (emit-lap `(SET ,proc (UNKNOWNCALL ,(vname proc) ,(length argl))))
	  (emit-lap `(SET ,(vname loc)
			  ((VIA (PROCEDURE_CODE ,(vname proc)))
			   ,@argtemps
			   (PROCEDURE_CLOSURE ,(vname proc)))))
	  (restore-lap-temps save-state)))

;;; Argument lists are evaluated and loaded into temporary variables by the
;;; following function.  It returns a list of variables which hold the
;;; values.

(define (LOAD-ARGL argl bindings)
    (map (lambda (arg)
		 (if (and (symbol? arg)
			  (or (var-in-stack arg)
			      (and (var-is-global arg) (not (id-type arg)))
			      (var-is-constant arg)))
		     (begin 
			    (emit-extern arg)
			    (lookup arg bindings))
		     (let ((temp (use-lap-temp)))
			  (exp-genc temp arg bindings)
			  temp)))
	 argl))
