cmp: cmppass1-call: refactor first pass operators funcall and apply

This commit is contained in:
Daniel Kochmański 2023-06-09 13:18:01 +02:00
parent 18e1a69ba7
commit a35b89866a

View file

@ -20,51 +20,72 @@
(defun unoptimized-funcall (fun arguments)
(let ((l (length arguments)))
(if (<= l si:c-arguments-limit)
(make-c1form* 'FUNCALL :sp-change t :side-effects t
:args (c1expr fun) (c1args* arguments))
(make-c1form* 'CL:FUNCALL :sp-change t :side-effects t
:args (c1expr fun) (c1args* arguments))
(unoptimized-long-call fun arguments))))
(defun macroexpand-lambda-block (lambda)
(if (eq (first lambda) 'EXT::LAMBDA-BLOCK)
(macroexpand-1 lambda)
lambda))
(defun optimized-lambda-call (lambda-form arguments apply-p)
(multiple-value-bind (bindings body)
(transform-funcall/apply-into-let* lambda-form arguments apply-p)
`(let* ,bindings ,@body)))
(defun try-optimized-lambda-call (fun args apply-p)
(unless (consp fun)
(return-from try-optimized-lambda-call nil))
(when (function-form-p fun)
(setf fun (second fun)))
(if (and (consp fun)
(member (first fun) '(cl:lambda ext:lambda-block)))
(optimized-lambda-call fun args apply-p)
nil))
(defun try-macro-expression-call (fun args)
(unless (consp fun)
(return-from try-macro-expression-call nil))
(let* ((name (first fun))
(fd (and (symbolp name)
;; We do not want to macroexpand 'CL:THE
(not (eq name 'CL:THE))
(cmp-macro-function name))))
(if fd
(c1funcall (list* (cmp-expand-macro fd fun) args))
nil)))
(defun try-function-special-call (fun args)
(unless (function-form-p fun)
(return-from try-function-special-call nil))
(let ((fname (second fun)))
(if (si:valid-function-name-p fname)
(c1call fname args nil)
(cmperr "Malformed function name: ~A." fun))))
(defun not-a-closure-p (fname)
(declare (si::c-local))
(not (and (fboundp fname) (nth-value 1 (function-lambda-expression (fdefinition fname))))))
(defun function-form-p (form)
(declare (si::c-local))
(if (and (consp form)
(eq (first form) 'CL:FUNCTION))
(prog1 t
(check-args-number 'CL:FUNCTION (rest form) 1 1))
nil))
(defun c1funcall (args)
(check-args-number 'FUNCALL args 1)
(let ((fun (first args))
(arguments (rest args))
fd)
(cond ;; (FUNCALL (LAMBDA ...) ...) or (FUNCALL (EXT::LAMBDA-BLOCK ...) ...)
((and (consp fun)
(member (first fun) '(LAMBDA EXT::LAMBDA-BLOCK)))
(optimize-funcall/apply-lambda (macroexpand-lambda-block fun)
arguments nil))
;; (FUNCALL atomic-expression ...)
((atom fun)
(unoptimized-funcall fun arguments))
;; (FUNCALL macro-expression ...)
((let ((name (first fun)))
(setq fd (and (symbolp name)
;; We do not want to macroexpand 'THE
(not (eq name 'THE))
(cmp-macro-function name))))
(c1funcall (list* (cmp-expand-macro fd fun) arguments)))
;; (FUNCALL lisp-expression ...)
((not (eq (first fun) 'FUNCTION))
(unoptimized-funcall fun arguments))
;; (FUNCALL #'GENERALIZED-FUNCTION-NAME ...)
((si::valid-function-name-p (setq fun (second fun)))
(c1call fun arguments nil))
;; (FUNCALL #'(LAMBDA ...) ...) or (FUNCALL #'(EXT::LAMBDA-BLOCK ...) ...)
((and (consp fun)
(member (first fun) '(LAMBDA EXT::LAMBDA-BLOCK)))
(optimize-funcall/apply-lambda (macroexpand-lambda-block fun)
arguments nil))
(t
(cmperr "Malformed function name: ~A" fun)))))
(check-args-number 'CL:FUNCALL args 1)
(destructuring-bind (fun . arguments) args
(or ;; (FUNCALL (LAMBDA ...) ...) or (FUNCALL (EXT:LAMBDA-BLOCK ...) ...)
;; (FUNCALL #'(LAMBDA ...) ...) or (FUNCALL #'(EXT:LAMBDA-BLOCK ...) ...)
(try-optimized-lambda-call fun arguments nil)
;; (FUNCALL macro-expression ...)
(try-macro-expression-call fun arguments)
;; (FUNCALL (FUNCTION function-name) ...)
(try-function-special-call fun arguments)
;; (FUNCALL lisp-expression ...) or (FUNCALL atomic-expression ...)
(unoptimized-funcall fun arguments))))
(defun c1multiple-value-call (args &aux forms)
(check-args-number 'MULTIPLE-VALUE-CALL args 1)
(check-args-number 'CL:MULTIPLE-VALUE-CALL args 1)
(cond
;; (M-V-C #'FUNCTION) => (FUNCALL #'FUNCTION)
((endp (rest args))
@ -72,7 +93,7 @@
;; (M-V-C #'FUNCTION (VALUES A ... Z)) => (FUNCALL #'FUNCTION A ... Z)
((and (= (length args) 2)
(consp (setq forms (second args)))
(eq 'VALUES (first forms)))
(eq 'CL:VALUES (first forms)))
(c1funcall (list* (first args) (rest forms))))
;; More complicated case.
(t
@ -85,35 +106,23 @@
(si::apply-from-stack-frame ,frame ,function)))))))
(defun c1apply (args)
(check-args-number 'APPLY args 2)
(check-args-number 'CL:APPLY args 2)
(flet ((default-apply (fun arguments)
(let ((form (c1funcall (list* '#'APPLY fun arguments))))
(when (and (consp fun) (eq (first fun) 'FUNCTION))
(when (function-form-p fun)
(let* ((fname (second fun))
(type (get-return-type fname)))
(when type
(setf (c1form-type form) type))))
form)))
(let* ((fun (first args))
(arguments (rest args)))
(cond ((eql (first (last arguments)) 'clos:.combined-method-args.)
;; Uses frames instead of lists as last argumennt
(default-apply fun arguments))
((and (consp fun)
(member (first fun) '(LAMBDA EXT::LAMBDA-BLOCK)))
(optimize-funcall/apply-lambda (macroexpand-lambda-block fun)
arguments t))
((and (consp fun)
(eq (first fun) 'FUNCTION)
(consp (second fun))
(member (caadr fun) '(LAMBDA EXT::LAMBDA-BLOCK)))
(c1apply (list* (second fun) arguments)))
(t
(default-apply fun arguments))))))
(defun not-a-closure-p (fname)
(declare (si::c-local))
(not (and (fboundp fname) (nth-value 1 (function-lambda-expression (fdefinition fname))))))
form))
(last-argument-is-frame-p (arguments)
;; Uses frames instead of lists as last argument. ;; gross hack!
(eql (first (last arguments)) 'clos:.combined-method-args.)))
(destructuring-bind (fun . arguments) args
(if (last-argument-is-frame-p arguments)
(default-apply fun arguments)
(or (try-optimized-lambda-call fun arguments t)
(default-apply fun arguments))))))
(defun c1call (fname args macros-allowed &aux fd success can-inline)
(cond ((> (length args) si::c-arguments-limit)
@ -151,9 +160,8 @@
(not (fun-closure fd)))
(cmpnote "Inlining ~a" fname)
(inline-local (fun-lambda-expression fd) fd args))
((and (consp can-inline)
(not-a-closure-p fname)
(eq (first can-inline) 'function))
((and (function-form-p can-inline)
(not-a-closure-p fname))
(let ((*inline-max-depth* (1- *inline-max-depth*)))
(cmpnote "Inlining ~a" fname)
`(funcall ,can-inline ,@args)))
@ -167,8 +175,7 @@
(*cmp-env* (cmp-env-copy)))
;; To inline the function, we transform it into a let* statement.
(multiple-value-bind (bindings body)
(transform-funcall/apply-into-let* (macroexpand-lambda-block lambda)
args nil)
(transform-funcall/apply-into-let* lambda args nil)
(multiple-value-bind (let-vars let-inits specials other-decls body)
(process-let-bindings 'LET* bindings body)
;; We have to compile the function body in the same
@ -251,6 +258,8 @@
(defun transform-funcall/apply-into-let* (lambda-form arguments apply-p
&aux apply-list apply-var
let-vars extra-stmts all-keys)
(when (eq (first lambda-form) 'ext:lambda-block)
(setf lambda-form (macroexpand-1 lambda-form)))
(multiple-value-bind (requireds optionals rest key-flag keywords
allow-other-keys aux-vars)
(cmp-process-lambda-list (second lambda-form))