Skip to content
cmplam.lsp 21.7 KiB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
;;;;  -*- Mode: Lisp; Syntax: Common-Lisp; Package: C -*-
;;;;
;;;;  Copyright (c) 1984, Taiichi Yuasa and Masami Hagiya.
;;;;  Copyright (c) 1990, Giuseppe Attardi.
;;;;
;;;;    This program is free software; you can redistribute it and/or
;;;;    modify it under the terms of the GNU Lesser General Public
;;;;    License as published by the Free Software Foundation; either
;;;;    version 3 of the License, or (at your option) any later version.
;;;;
;;;;    See file '../../Copyright' for full details.

;;;; CMPLAM  Lambda expression.

(in-package "COMPILER")

;;; (defvar *show-args* nil)

;;; During Pass1, a lambda-list
;;;
;;; (	{ var }*
;;; 	[ &optional { var | ( var [ initform [ svar ] ] ) }* ]
;;; 	[ &rest var ]
;;; 	[ &key { var | ( { var | ( kwd var ) } [initform [ svar ]])}*
;;; 		[&allow-other-keys]]
;;; 	[ &aux {var | (var [initform])}*]
;;; )
;;;
;;; is transformed into
;;;
;;; (	( { var }* )				; required
;;; 	( { var initform svar }* )		; optional
;;; 	{ var | nil }				; rest
;;; 	allow-other-keys-flag
;;; 	( { kwd-vv-index var initform svar }* )	; key
;;; )
;;;
;;; where
;;; 	svar:	NIL	; means svar is not supplied
;;;	        | var
;;;
;;; &aux parameters will be embedded into LET*.
;;;
;;; c1lambda-expr receives
;;;	( lambda-list { doc | decl }* . body )
;;; and returns
;;;	( lambda info-object lambda-list' doc body' )
;;;
;;; Doc is NIL if no doc string is supplied.
;;; Body' is body possibly surrounded by a LET* (if &aux parameters are
;;; supplied) and an implicit block.

(defun c1lambda-doc (form)
  (second (c1form-args form)))

(defun c1lambda-body (form)
  (third (c1form-args form)))

(defun c1lambda-list (form)
  (first (c1form-args form)))

(defun fun-fixed-narg (fun)
  "Returns true if the function has a fixed number of arguments and it is not a closure.
The function thus belongs to the type of functions that mkcl_make_cfun accepts."
  (let (narg)
    (and ;;(not (eq (fun-closure fun) 'CLOSURE)) ;; JCB
	 (= (fun-minarg fun) (setf narg (fun-maxarg fun)))
	 (if (eq (fun-closure fun) 'CLOSURE) (< narg 5) t) ;; fast dispatch supports only 0 to 4 args. JCB
	 (<= narg si::c-arguments-limit)
	 narg)))

(defun fun-needs-narg (fun)
  (not (fun-fixed-narg fun)))

(defun add-referred-variables-to-function (fun var-list)
  (setf (fun-referred-vars fun)
	(set-difference (union (fun-referred-vars fun) var-list)
			(fun-local-vars fun)))
  fun)

(defun c1compile-function (lambda-list-and-body &key (fun (make-fun))
			   (name (fun-name fun)) (CB/LB 'CB))
  (setf (fun-name fun) name
	(fun-parent fun) *current-function*)
  (when *current-function*
    (push fun (fun-child-funs *current-function*)))
  (let* ((*current-function* fun)
	 (*cmp-env* (cmp-env-mark CB/LB))
	 (*lex-local-funs* nil) ;; JCB
	 (setjmps *setjmps*)
	 (decl (si::process-declarations (rest lambda-list-and-body)))
	 (lambda-expr (c1lambda-expr lambda-list-and-body
				     (si::function-block-name name)))
	 (children (fun-child-funs fun))
	 (global (and (assoc 'SI::C-GLOBAL decl) 'T))
	 (debug (search-optimization-quality decl 'debug))
	 cfun exported minarg maxarg)
    (unless (eql setjmps *setjmps*)
      (setf (c1form-volatile lambda-expr) t))
    (setf (fun-lambda fun) lambda-expr)
    (if global
	(multiple-value-setq (cfun exported) (exported-fname name))
	(setf cfun (next-cfun "LC~D~A" name) exported nil))
    (if exported
	;; Check whether the function was proclaimed to have a certain
	;; number of arguments, and otherwise produce a function with
	;; a flexible signature.
	(progn
	  (multiple-value-setq (minarg maxarg) (get-proclaimed-narg name))
	  (unless minarg
	    (setf minarg 0 maxarg call-arguments-limit)))
      (multiple-value-setq (minarg maxarg)
			   (lambda-form-allowed-nargs lambda-expr)))
    (setf (fun-cfun fun) cfun
	  (fun-global fun) global
	  (fun-exported fun) exported
	  (fun-closure fun) nil
	  (fun-minarg fun) minarg
	  (fun-maxarg fun) maxarg
	  (fun-description fun) name
	  (fun-lex-local-funs fun) *lex-local-funs* ;; JCB
	  (fun-debug fun) debug)
    (setf (fun-name-loc fun) (add-object (fun-name fun) :permanent t :duplicate t)) ;; JCB
    (setf (fun-block-index fun) (vector-push-extend fun *global-cfuns-array*)) ;; JCB

    (reduce #'add-referred-variables-to-function
	    (mapcar #'fun-referred-vars children)
	    :initial-value fun)
    (reduce #'add-referred-variables-to-function
	    (mapcar #'fun-referred-vars (fun-referred-funs fun))
	    :initial-value fun)
    (do ((finish nil))
	(finish)
      (setf finish t)
      (dolist (f (fun-child-funs fun))
	(when (compute-fun-closure-type f)
	  (setf finish nil))))
    (compute-fun-closure-type fun)
    (when global
      #+(or)
      (when (fun-closure fun)
	(error "Function ~A is global but is closed over some variables.~%~{~A ~}"
	       (fun-name fun) (mapcar #'var-name (fun-referred-vars fun))))
      (new-defun fun)))
  fun)

(defun c1lambda-expr (lambda-expr
                      &optional (block-name nil)
                      &aux doc body ss is iables ts dyns
                           other-decls
			   all-declarations
		           new-variables
		           (type-checks '())
			   (*permanent-data* t)
			   (old-env *cmp-env*)
                           (*cmp-env* (cmp-env-copy))
			   (*notinline* *notinline*))

  (cmpck (endp lambda-expr)
         "The lambda expression ~s is illegal." (cons 'LAMBDA lambda-expr))

  (multiple-value-setq (body ss ts is other-decls doc all-declarations iables dyns)
                       (c1body (cdr lambda-expr) t))

  (when block-name (setq body (list (cons 'BLOCK (cons block-name body)))))

  (multiple-value-bind (requireds nb_reqs optionals nb_opts rest key-flag keywords nb_keys
			allow-other-keys aux-vars)
      (si::process-lambda-list (car lambda-expr) 'function)
    (declare (ignore nb_reqs nb_opts nb_keys))
    (do ((specs requireds (cdr specs)))
	((endp specs))
      (let* ((name (first specs))
	     (var (c1make-var name ss is ts iables dyns)))
	(push var type-checks)
	(setf (first specs) var)
	(push-vars var)))

    (do ((specs optionals (cdddr specs)))
	((endp specs))
      (let* ((name (first specs))
	     (var (c1make-var name ss is ts iables dyns))
	     (init (second specs))
	     (flag (third specs)))
	(setq init (if init
		       (and-form-type (var-type var) (c1expr init) init
				      :safe "In (LAMBDA ~a...)" block-name)
		       (default-init var)))
	(push var type-checks)
	(push-vars var)
	(when flag
	  (push-vars (setq flag (c1make-var flag ss is ts iables dyns))))
	(setf (first specs) var
	      (second specs) init
	      (third specs) flag)))

    (when rest
      (push-vars (setq rest (c1make-var rest ss is ts iables dyns))))

    (do ((specs keywords (cddddr specs)))
	((endp specs))
      (let* ((key (first specs))
	     (name (second specs))
	     (var (c1make-var name ss is ts iables dyns))
	     (init (third specs))
	     (flag (fourth specs)))
	(declare (ignore key))
	(setq init (if init
		       (and-form-type (var-type var) (c1expr init) init
				      :safe "In (LAMBDA ~a...)" block-name)
		       (default-init var)))
	(push var type-checks)
	(push-vars var)
	(when flag
	  (push-vars (setq flag (c1make-var flag ss is ts iables dyns))))
	(setf (second specs) var
	      (third specs) init
	      (fourth specs) flag)))

    (c1add-declarations other-decls) ;; some of these decls need to take effect right now.  JCB

    ;; After creating all variables and processing the initalization
    ;; forms, we wil process the body. However, all free declarations,
    ;; that is declarations which do not refer to the function
    ;; arguments, have to be applied to the body. At the same time, we
    ;; replace &aux variables with a LET* form that defines them.
    (let* ((declarations other-decls)
	   (new-variables (cmp-env-new-variables *cmp-env* old-env))
	   (new-variable-names (mapcar #'var-name new-variables)))
      (when (setq ss (set-difference ss new-variable-names))
	(push `(special ,@ss) declarations))
      (when (setq is (set-difference is new-variable-names))
	(push `(ignorable ,@is) declarations))
      (loop for (var . type) in ts
	    unless (member var new-variable-names)
	    do (push `(type ,type ,var) declarations))
      ;; We generate automatic type checks for function arguments that
      ;; are declared These checks can be deactivated by appropriate
      ;; safety settings which are checked by OPTIONAL-CHECK-TYPE. Note
      ;; that not all type declarations can be checked (take for instance
      ;; (type (function (t t) t) foo)) We let OPTIONAL-CHECK-TYPE do the
      ;; job.
      ;;
      (let* ((pairs (loop for var in type-checks
			  nconc (let* ((name (var-name var))
				       (type (assoc name ts)))
				  (when type
				    (if other-decls
					(loop for decl in other-decls
					      unless (and (consp decl) ;; unless we're told not to check.
							  (eq (first decl) 'si::no-check-type)
							  (member name (rest decl)))
					      do (return (list (list name (cdr type)))))
				      (list (list name (cdr type)))))))))
	(when (and pairs (policy-automatic-check-type-p))
	  (cmpnote"In ~:[an anonymous function~;function ~:*~A~], checking types of argument~@[s~]~{ ~A~}."
		   block-name
		   (mapcar #'var-name type-checks))
	  (loop for pair in (nreverse pairs)
	     do (push `(optional-check-type ,@pair) body))))
      (setq body
	    (cond (aux-vars
		   (let ((let nil))
		     (do ((specs aux-vars (cddr specs)))
			 ((endp specs))
		       (let ((var (first specs))
			     (init (second specs)))
			 (setq let (cons (if init (list var init) var) let))))
		     (c1expr `(let* ,(nreverse let)
			       (declare ,@declarations)
			       ,@body))))
		  (declarations
		   (c1expr `(locally (declare ,@declarations) ,@body)))
		  (t
		   (c1progn body))))
      (dolist (var new-variables)
	(check-vref var))
      (make-c1form* 'LAMBDA
		    :local-vars new-variables
 		    :args (list requireds optionals rest key-flag keywords
				allow-other-keys)
		    doc body))))

(defun lambda-form-allowed-nargs (lambda)
  (let ((minarg 0)
	(maxarg call-arguments-limit))
    (destructuring-bind (requireds optionals rest key-flag keywords a-o-k)
	(c1form-arg 0 lambda)
      (declare (ignore keywords))
      (when (and (null rest) (not key-flag) (not a-o-k))
	(setf minarg (length requireds)
	      maxarg (+ minarg (/ (length optionals) 3)))))
    (values minarg maxarg)))

#| Steps:
 1. defun creates declarations for requireds + va_alist
 2. c2lambda-expr adds declarations for:
	unboxed requireds
	lexical optionals (+ supplied-p), rest, keywords (+ supplied-p)
    Lexical optionals and keywords can be unboxed if:
	a. there is more then one reference in the body
	b. they are not referenced in closures
 3. binding is performed for:
	special or unboxed requireds
	optionals, rest, keywords
 4. the function name is optionally pushed onto the IHS when
    the caller asks for it.
|#

(defun c2lambda-expr
    (lambda-list body cfun fname use-narg fname-in-ihs-p
                 &optional closure-type local-entry-p ;; local-entry-p is unused and useless. JCB
		 &aux
		 (requireds (first lambda-list))
		 (optionals (second lambda-list))
		 (rest (third lambda-list))
		 rest-loc
		 (keywords (fifth lambda-list))
		 (allow-other-keys (sixth lambda-list))
		 vars-for-debug
		 (nreq (length requireds))
		 (nopt (/ (length optionals) 3))
		 (nkey (/ (length keywords) 4))
		 (varargs (or optionals rest keywords allow-other-keys))
		 simple-varargs
		 (*permanent-data* t)
		 (*unwind-exit* *unwind-exit*)
		 (*env* *env*)
		 (last-arg))
  (declare (fixnum nreq nkey))

  (if (and fname ;; named function
	   ;; no required appears in closure,
	   (dolist (var (car lambda-list) t)
	     (declare (type (or null var) var))
	     (when (var-ref-ccb var) (return nil)))
	   (null (second lambda-list))	;; no optionals,
	   (null (third lambda-list))	;; no rest parameter, and
	   (null (fourth lambda-list)))	;; no keywords.
      (setf *tail-recursion-info* (cons *tail-recursion-info* (car lambda-list)))
    (setf *tail-recursion-info* nil))

  ;; For local entry functions arguments are processed by t3defun.
  ;; They must have a fixed number of arguments, no optionals, rest, etc.
  (when (and local-entry-p varargs)
    (baboon))

  ;; check arguments
  (unless (or local-entry-p (not (compiler-check-args)))
    (incf *inline-blocks*)
    (if (and use-narg (not varargs))
	(wt-nl "if(narg!=" nreq ") mkcl_FEwrong_num_arguments_anonym(env);")
	(when varargs
	  (when requireds
	    (wt-nl "if(narg<" nreq ") mkcl_FEwrong_num_arguments_anonym(env);"))
	  (unless (or rest keywords allow-other-keys)
	    (wt-nl "if(narg>" (+ nreq nopt) ") mkcl_FEwrong_num_arguments_anonym(env);"))))
    (wt-nl "{"))

  ;; If the number of required arguments exceeds the number of variables we
  ;; want to pass on the C stack, we pass some of the arguments to the list
  ;; of optionals, which will eventually get passed in the lisp stack.
  (when (> nreq si::c-arguments-limit)
    (setf nopt (+ nopt (- nreq si::c-arguments-limit))
	  nreq si::c-arguments-limit)
    (setf optionals (nconc (reduce #'nconc (mapcar #'(lambda (var) (list var *c1nil* NIL))
						   (subseq requireds si::c-arguments-limit)))
				  (rest optionals))
	  requireds (subseq requireds 0 si::c-arguments-limit)
	  varargs t))

  ;; For each variable, set its var-loc.
  ;; For optional and keyword parameters, and lexical variables which
  ;; can be unboxed, this will be a new LCL.
  ;; The bind step later will assign to such variable.
  (let* ((req0 *lcl*)
	 (lcl (+ req0 nreq)))
    (declare (fixnum lcl))
    (labels ((wt-decl (var)
               (wt-nl)
               (wt *volatile* (rep-type-name (var-rep-type var)) " ")
               (wt-lcl (incf lcl)) (wt ";")
               `(LCL ,lcl))
             (do-decl (var)
	       (when (local var) ; no LCL needed for SPECIAL or LEX
		 (setf (var-loc var) (wt-decl var)))))
      (do ((reqs requireds (cdr reqs))
	   (reqi (1+ req0) (1+ reqi)))
	  ((endp reqs))
	(declare (fixnum reqi) (type cons reqs))
	(let ((var (first reqs)))
	  (declare (type var var))
	  (cond (local-entry-p
		 (bind `(LCL ,reqi) var))
		((unboxed var) ; create unboxed variable
		 (setf (var-loc var) (wt-decl var))))))
      (when (and rest (< (var-ref rest) 1)) ; dont create rest if not used
	(setq rest nil))
      (do ((opt optionals (cdddr opt)))
	  ((endp opt))
        (do-decl (first opt))
        (when (third opt) (do-decl (third opt))))
      (when rest (setq rest-loc (wt-decl rest)))
      (do ((key keywords (cddddr key)))
	  ((endp key))
        (do-decl (second key))
        (when (fourth key) (do-decl (fourth key)))))

    (when varargs
      (let ((first-arg (cond ((plusp nreq) (format nil "V~d" (+ req0 nreq)))
			     ((eq closure-type 'LEXICAL) (format nil "lex~D" (1- *level*)))
			     (t "narg"))))
	(wt-nl
	  (format nil
	     (if (setq simple-varargs (and (not (or rest keywords allow-other-keys))
					   (< (+ nreq nopt) 30)))
		 "va_list args; va_start(args,~a);"
		 "mkcl_va_list args; mkcl_va_start(env, args,~a,narg,~d);")
	     first-arg nreq))))

    ;; Bind required parameters.
    (do ((reqs requireds (cdr reqs))
	 (reqi (1+ req0) (1+ reqi)))	; to allow concurrent compilations
	((or local-entry-p (endp reqs)))
      (declare (fixnum reqi) (type cons reqs))
      (bind `(LCL ,reqi) (first reqs)))

    (when fname-in-ihs-p
      (push 'IHS *unwind-exit*)
      (when (>= *debug-fun* 3)
	(dolist (req requireds)
	  (push req vars-for-debug))))

    (setq *lcl* lcl))

  ;; Bind optional parameters as long as there remain arguments.
  (when optionals
    ;; When binding optional values, we use two calls to BIND. This means
    ;; 'BDS-BIND is pushed twice on *unwind-exit*, which results in two calls
    ;; to bds_unwind1(), which is wrong. A simple fix is to save *unwind-exit*
    ;; which is what we do here.
    (let ((va-arg-loc (if simple-varargs 'VA-ARG 'CL-VA-ARG)))
      ;; counter for optionals
      (wt "{int i=" nreq ";")
      (do ((opt optionals (cdddr opt)))
	  ((endp opt))
	(wt-nl "if (i >= narg) {")
	  (bind-init (second opt) (first opt))
	  (when (third opt) (bind nil (third opt)))
	(wt-nl "} else {")
	  (wt-nl "i++;")
	  (let ((*unwind-exit* *unwind-exit*))
	    (bind va-arg-loc (first opt)))
	  (when (third opt) (bind t (third opt)))
	(wt-nl "}")
	(when (>= *debug-fun* 3)
	  (push (first opt) vars-for-debug)
	  (when (third opt) (push (third opt) vars-for-debug))
	  )
	)
      (wt "}"))
    )

  (when (or rest keywords allow-other-keys)
    (let ((rest-is-dynamic nil))
      (when (and rest (eq 'DYNAMIC (var-extent rest)))
	(if (eq 'CLOSURE (var-kind rest))
	    (cmpwarn-style "Declaration DYNAMIC-EXTENT cannot be applied to closure variable ~S. Declaration ignored" (var-name rest))
	  (let ((marker (c1make-global-variable 'si::*dynamic-cons-stack* :kind 'SPECIAL :type T)))
	    (bds-bind marker marker) ;; Mark the stack here before adding to it. JCB
	    (setq rest-is-dynamic T)
	    )
	  )
	)
      (cond ((not (or keywords allow-other-keys))
	     (wt-nl rest-loc "=mkcl_grab_rest_args(env, args, " (if rest-is-dynamic "TRUE);" "FALSE);")))
	    (t
	     (cond (keywords
		    (wt-nl "{ mkcl_object keyvars[" (* 2 nkey) "];")
		    (wt-nl "mkcl_parse_key(env, args," nkey "," cfun "keys,keyvars"))
		   (t
		    (wt-nl "mkcl_parse_key(env, args,0,NULL,NULL")))
	     ;; This explicit coercion is required to remove the "volatile"
	     ;; declaration on some variables.
	     (if rest (wt ",(mkcl_object*)&" rest-loc) (wt ",NULL"))
	     (wt (if allow-other-keys ",TRUE," ",FALSE,")
		 (if rest-is-dynamic "TRUE);" "FALSE);")))))
    (when rest (bind rest-loc rest)))

  (when varargs
    (wt-nl (if simple-varargs "va_end(args);" "mkcl_va_end(args);")))

  ;;; Bind keywords.
  (do ((kwd keywords (cddddr kwd))
       (all-kwd nil)
       (KEYVARS[i] `(KEYVARS 0))
       (i 0 (1+ i)))
      ((endp kwd)
       (when all-kwd
	 (wt-nl-h "#define " cfun "keys (&" (add-keywords (nreverse all-kwd)) ")")
	 (wt-nl "}")))
    (declare (fixnum i))
    (push (first kwd) all-kwd)
    (let ((key (first kwd))
	  (var (second kwd))
	  (init (third kwd))
	  (flag (fourth kwd)))
      (declare (ignore key))
      (cond ((and (eq (c1form-name init) 'LOCATION)
		  (null (c1form-arg 0 init)))
	     ;; no initform
	     ;; mk_cl_Cnil has been set in keyvars if keyword parameter is not supplied.
	     (setf (second KEYVARS[i]) i)
	     (bind KEYVARS[i] var))
	    (t
	     ;; with initform
	     (setf (second KEYVARS[i]) (+ nkey i))
	     (wt-nl "if(") (wt-loc KEYVARS[i]) (wt "==mk_cl_Cnil){")
	     (let ((*unwind-exit* *unwind-exit*))
	       (bind-init init var))
	     (wt-nl "}else{")
	     (setf (second KEYVARS[i]) i)
	     (bind KEYVARS[i] var)
	     (wt "}")))
      (when flag
	(setf (second KEYVARS[i]) (+ nkey i))
	(bind KEYVARS[i] flag))
      (when (>= *debug-fun* 3)
	(push var vars-for-debug)
	(when flag (push flag vars-for-debug))
	)
      )
    )

  (when (>= *debug-fun* 3)
    (when rest (push rest vars-for-debug))
    (wt-nl "{")
    (build-debug-lexical-env vars-for-debug)
    )

  (when *tail-recursion-info*
    (push 'TAIL-RECURSION-MARK *unwind-exit*)
    (wt-nl1 "TTL:"))

  ;;; Now the parameters are ready, after all!
  (c2expr body)

  (when (>= *debug-fun* 3)
    (wt-nl "}")
    )
  ;;; Closing braces is done in cmptop.lsp
  )

(defun optimize-funcall/apply-lambda (lambda-form arguments apply-p
				      &aux body apply-list apply-var
				      let-vars extra-stmts all-keys)
  (multiple-value-bind (requireds nb_reqs optionals nb_opts rest key-flag keywords nb_keys
				  allow-other-keys aux-vars)
      (si::process-lambda-list (car lambda-form) 'function)
    (declare (ignore nb_reqs nb_opts nb_keys aux-vars))
    (when apply-p
      (setf apply-list (first (last arguments))
	    apply-var (gensym)
	    arguments (butlast arguments)))
    (setf arguments (copy-list arguments))
    (do ((scan arguments (cdr scan)))
	((endp scan))
      (let ((form (first scan)))
	(unless (constantp form)
	  (let ((aux-var (gensym)))
	    (push `(,aux-var ,form) let-vars)
	    (setf (car scan) aux-var)))))
    (when apply-var
      (push `(,apply-var ,apply-list) let-vars))
    (dolist (i requireds)
      (push (list i
		  (cond (arguments
			 (pop arguments))
			(apply-p
			 `(if ,apply-var
			   (pop ,apply-var)
			   (si::dm-too-few-arguments)))
			(t
			 (error 'SI:SIMPLE-PROGRAM-ERROR
				:format-control "Too few arguments for lambda form ~S"
				:format-arguments (cons 'LAMBDA lambda-form)))))
	    let-vars))
    (do ((scan optionals (cdddr scan)))
	((endp scan))
      (let ((opt-var (first scan))
	    (opt-flag (third scan))
	    (opt-value (second scan)))
	(cond (arguments
	       (setf let-vars
		     (list* `(,opt-var ,(pop arguments))
			    `(,opt-flag t)
			    let-vars)))
	      (apply-p
	       (setf let-vars
		     (list* `(,opt-var (if ,apply-var
					   (pop ,apply-var)
					   ,opt-value))
			    `(,opt-flag ,apply-var)
			    let-vars)))
	      (t
	       (setf let-vars
		     (list* `(,opt-var ,opt-value)
			    `(,opt-flag nil)
			    let-vars))))))
    (when (or key-flag allow-other-keys)
      (unless rest
	(setf rest (gensym))))
    (when rest
      (push `(,rest ,(if arguments
			 (if apply-p
			     `(list* ,@arguments ,apply-var)
			     `(list ,@arguments))
			 (if apply-p apply-var nil)))
	    let-vars))
    (do ((scan keywords (cddddr scan)))
	((endp scan))
      (let ((keyword (first scan))
	    (key-var (second scan))
	    (key-value (third scan))
	    (key-flag (or (fourth scan) (gensym))))
	(push keyword all-keys)
	(setf let-vars
	      (list*
	       `(,key-var (if (eq ,key-flag 'si::failed) ,key-value ,key-flag))
	       `(,key-flag (si::search-keyword ,rest ,keyword))
	       let-vars))
	(when (fourth scan)
	  (push `(setf ,key-flag (not (eq ,key-flag 'si::failed)))
		extra-stmts))))
    (when (and key-flag (not allow-other-keys))
      (push `(si::check-keyword ,rest ',all-keys) extra-stmts))
    `(let* ,(nreverse (delete-if-not #'first let-vars))
      ,@(multiple-value-bind (decl body)
	   (si::find-declarations (rest lambda-form))
	 (append decl extra-stmts body)))))