cmp: disable inlining for global functions that are closures

For functions already compiled and loaded, we simply check if the
definition is a closure. For functions defined in the same file, we
don't store their definition in the compiler environment but instead
use *global-funs*. The advantage is that this directly allows us to
determine whether a function is a closure or not and we don't have to
run the first compiler pass again each time we inline the function.

This commit also fixes some minor issues with the inline policy,
described in detail as follows:

1. The inline policy differed subtly between `proclaim` and `declaim`.
If a file like

(eval-when (:compile-toplevel)
  (proclaim '(inline f)))
(defun f ...)

was compiled (but not loaded), subsequent compilations would inline
`f` but for

(declaim (inline f))
(defun f ...)

the function `f` would only get inlined if the file was compiled _and_
loaded. We now use the latter approach for both cases. Thus, calling
`compile-file` without `load` has no side-effects regarding whether
functions are inlined or not.

2. We did not distinguish between functions which were declared inline
at a global versus local level such that e.g. in

(locally
    (declare (inline f))
  (defun f ...))

the function f would get inlined outside the scope of the `locally`
form. This is changed now such that local inline declarations only
apply to the scope in which they are made.

3. Inline declarations were made by expanding into statements like

(eval-when (:compile-toplevel)
  (c::declare-inline ...))

during the macroexpansion of `defun`. However this only works if the
`defun` appears at the toplevel and hence in code like

(declaim (inline f))
(let (...)
  (defun f ...))

the function `f` could not get inlined later on in the same file. This
is fixed now by calling the code which should run during compilation
directly when macro expanding defun.
This commit is contained in:
Marius Gerbershagen 2022-10-07 21:20:26 +02:00
parent 4da9cc3a87
commit fdcf5e7eff
4 changed files with 95 additions and 43 deletions

View file

@ -11,7 +11,7 @@
;;;;
;;;; See file '../Copyright' for full details.
;;;;
;;;; CMPTYPE-PROP -- Type propagation basic routines and database
;;;; CMPENV-FUN -- Declarations concerning function types and inlining
;;;;
(in-package "COMPILER")
@ -159,25 +159,21 @@
(defun inline-possible (fname &optional (env *cmp-env*))
(not (declared-notinline-p fname env)))
;;; Install inline expansion of function. If the function is
;;; PROCLAIMED inline, then we keep a copy of the definition as a
;;; symbol property. If the function is DECLAIMED inline, then we keep
;;; the definition in the compiler environment during compilation and
;;; install it as a symbol property during loading of the compiled
;;; file.
(defun maybe-install-inline-function (fname form env)
(let* ((x (cmp-env-search-declaration 'inline env))
(flag (assoc fname x :test #'same-fname-p))
(declared (and flag (cdr flag)))
(proclaimed (sys:get-sysprop fname 'inline)))
`(progn
,(when declared
`(progn
(eval-when (:compile-toplevel)
(c::declare-inline ',fname *cmp-env-root* ',form))
(eval-when (:load-toplevel :execute)
(si:put-sysprop ',fname 'inline ',form))))
,(when proclaimed
`(eval-when (:compile-toplevel :load-toplevel :execute)
(si:put-sysprop ',fname 'inline ',form))))))
;;; Install inline expansion of function.
(defun maybe-install-inline-function (fname form)
(when (declared-inline-p fname *cmp-env-root*)
;; The function was already PROCLAIMED inline and might be
;; redefined in the file we are currently compiling. Declare it as
;; inline in the compiler environment and remove the symbol
;; property so that if we can't inline the new definition (e.g.
;; because it is a closure) we don't accidentally inline an old
;; definition from the symbol property.
(declare-inline fname *cmp-env-root*)
(si:rem-sysprop fname 'inline)
;; If the function is PROCLAIMED or DECLAIMED inline, then we
;; install the definition as a symbol property during loading of
;; the compiled file. If the function was only DECLARED inline
;; locally we don't keep the definition.
`(eval-when (:load-toplevel :execute)
(si:put-sysprop ',fname 'inline ',form))))

View file

@ -116,6 +116,10 @@
(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))))))
(defun c1call (fname args macros-allowed &aux fd success can-inline)
(cond ((> (length args) si::c-arguments-limit)
(if (and macros-allowed
@ -147,13 +151,19 @@
(setq fd (macro-function fname)))
(cmp-expand-macro fd (list* fname args)))
((and (setq can-inline (declared-inline-p fname))
(consp can-inline)
(eq (first can-inline) 'function)
(plusp *inline-max-depth*)
(<= (cmp-env-optimization 'space) 1))
(let ((*inline-max-depth* (1- *inline-max-depth*)))
(cmpnote "Inlining ~a" fname)
`(funcall ,can-inline ,@args)))
(cond ((and (setq fd (find fname *global-funs* :key #'fun-name :test #'same-fname-p))
(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))
(let ((*inline-max-depth* (1- *inline-max-depth*)))
(cmpnote "Inlining ~a" fname)
`(funcall ,can-inline ,@args)))
(t (c1call-global fname args))))
(t (c1call-global fname args))))
(defun inline-local (lambda fun args)