cmp: add support for precompiled header files

Improves compilation speed for single functions by about 40-50
percent. Precompiled headers are specific to the compiler version and
options in use. Due to this, we regenerate the header whenever the
compiler configuration changes.
This commit is contained in:
Marius Gerbershagen 2020-04-18 21:25:38 +02:00
parent 46e158e912
commit 636cb4cf60
4 changed files with 87 additions and 0 deletions

View file

@ -32,6 +32,9 @@
** Enhancements ** Enhancements
** Issues fixed ** Issues fixed
- The generational and precise garbage collector modes work again - The generational and precise garbage collector modes work again
- ECL can now use precompiled headers to speed up compilation. Use ~(setq
c::*use-precompiled-headers* nil)~ to disable this feature
** Issues fixed
** API changes ** API changes
* 20.4.24 changes since 16.1.3 * 20.4.24 changes since 16.1.3
** Announcement ** Announcement

View file

@ -212,6 +212,11 @@ slashes before special characters.")
and standalone programs. It is not required to surround values with quotes or use and standalone programs. It is not required to surround values with quotes or use
slashes before special characters.") slashes before special characters.")
(defvar *use-precompiled-headers* #+msvc nil #-msvc t
"This variable controls whether the C compiler uses precompiled header files.")
(defvar *precompiled-header-flags* nil)
(defvar *precompiled-header-cc-config* nil)
;;; ;;;
;;; Compiler program and flags. ;;; Compiler program and flags.
;;; ;;;

View file

@ -65,6 +65,7 @@ the environment variable TMPDIR to a different value." template))
(:program (setf format +executable-file-format+)) (:program (setf format +executable-file-format+))
#+msvc #+msvc
(:import-library (setf extension "implib")) (:import-library (setf extension "implib"))
(:precompiled-header (setf format #-msvc "~a.h.gch" #+msvc "~a.pch"))
((:fasl :fas) (setf extension "fas"))) ((:fasl :fas) (setf extension "fas")))
(cond ((not (member output-file '(T NIL))) (cond ((not (member output-file '(T NIL)))
output-file) output-file)
@ -973,6 +974,7 @@ from the C language code. NIL means \"do not create the file\"."
(safe-run-program (safe-run-program
*cc* *cc*
`("-I." `("-I."
,@(precompiled-header-flags)
,(concatenate 'string "-I" (fix-for-mingw (ecl-include-directory))) ,(concatenate 'string "-I" (fix-for-mingw (ecl-include-directory)))
,@(split-program-options *cc-flags*) ,@(split-program-options *cc-flags*)
,@(and (>= (cmp-env-optimization 'speed) 2) ,@(and (>= (cmp-env-optimization 'speed) 2)
@ -1001,6 +1003,78 @@ from the C language code. NIL means \"do not create the file\"."
(declare (ignore options)) (declare (ignore options))
`(progn ,@body)) `(progn ,@body))
(defun need-to-dump-precompiled-header ()
(let* ((config *precompiled-header-cc-config*)
(need-to-dump (or (null config)
(not (eq (svref config 0) *cc*))
(not (eq (svref config 1) (ecl-include-directory)))
(not (eq (svref config 2) *cc-flags*))
(not (eq (svref config 3) *cc-optimize*))
(not (eq (svref config 4) *user-cc-flags*)))))
(when need-to-dump
(setf *precompiled-header-cc-config*
(vector *cc* (ecl-include-directory) *cc-flags*
*cc-optimize* *user-cc-flags*)))
need-to-dump))
(defun precompiled-header-flags ()
(when *use-precompiled-headers*
(when (need-to-dump-precompiled-header)
(handler-case
(dump-precompiled-header)
(error (err)
(setf *use-precompiled-headers* nil
*precompiled-header-flags* nil
*precompiled-header-cc-config* nil)
(cmpnote "Disabling precompiled header files due to error:~% ~A" err))))
*precompiled-header-flags*))
#+msvc
(defun dump-precompiled-header ()
;; The way precompiled headers work on msvc is not compatible with
;; what we want to use them for. The msvc compiler creates a
;; precompiled header file out of ordinary source files by
;; processing them up to a certain point at which all needed headers
;; are included. This creates both a precompiled header and a object
;; file. The object file created by this compilation must be
;; included in all binaries which are linked together from other
;; source files compiled using the precompiled header. Thus, we
;; would need to include the first object file created in a session
;; in all further object files if we wanted to support that.
(error "Precompiled headers are not supported for msvc."))
#-msvc
(defun dump-precompiled-header ()
(let* ((input-file (make-pathname
:directory (append (pathname-directory (ecl-include-directory))
'("ecl"))
:defaults (ecl-include-directory)
:name "ecl-cmp"
:type "h"))
(output-dir (merge-pathnames
(format nil "ecl-include~4,'0x/" (random #xffff))
(translate-logical-pathname "TMP:")))
(output-file (compile-file-pathname
(make-pathname :name "ecl-cmp" :defaults output-dir)
:type :precompiled-header)))
(ensure-directories-exist output-dir)
(push output-dir *files-to-be-deleted*)
(safe-run-program
*cc*
`("-x" "c-header"
,(fix-for-mingw (namestring input-file))
,(concatenate 'string "-I" (fix-for-mingw (ecl-include-directory)))
,@(split-program-options *cc-flags*)
,@(split-program-options *cc-optimize*)
"-o"
,(fix-for-mingw (namestring output-file))
,@(split-program-options *user-cc-flags*)))
(push output-file *files-to-be-deleted*)
(setf *precompiled-header-flags*
(list (concatenate 'string "-I" (namestring output-dir))
"-include"
(concatenate 'string (namestring output-dir) "ecl-cmp.h")))))
(ext:package-lock "CL" t) (ext:package-lock "CL" t)
(setf *features* (delete :ecl-bytecmp *features*)) (setf *features* (delete :ecl-bytecmp *features*))

View file

@ -16,6 +16,9 @@
See file '../Copyright' for full details. See file '../Copyright' for full details.
*/ */
#ifndef ECL_CMP_H
#define ECL_CMP_H
#ifndef __CYGWIN__ #ifndef __CYGWIN__
/* Recent versions of cygwin do not define fd_set when WINSOCKAPI is /* Recent versions of cygwin do not define fd_set when WINSOCKAPI is
* defined */ * defined */
@ -61,3 +64,5 @@ struct ecl_var_debug_info {
#define _ecl_check_narg(n) \ #define _ecl_check_narg(n) \
do { if (ecl_unlikely(narg != (n))) FEwrong_num_arguments_anonym();} while(0) do { if (ecl_unlikely(narg != (n))) FEwrong_num_arguments_anonym();} while(0)
#endif /* ECL_CMP_H */