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
** Issues fixed
- 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
* 20.4.24 changes since 16.1.3
** 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
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.
;;;

View file

@ -65,6 +65,7 @@ the environment variable TMPDIR to a different value." template))
(:program (setf format +executable-file-format+))
#+msvc
(:import-library (setf extension "implib"))
(:precompiled-header (setf format #-msvc "~a.h.gch" #+msvc "~a.pch"))
((:fasl :fas) (setf extension "fas")))
(cond ((not (member output-file '(T NIL)))
output-file)
@ -973,6 +974,7 @@ from the C language code. NIL means \"do not create the file\"."
(safe-run-program
*cc*
`("-I."
,@(precompiled-header-flags)
,(concatenate 'string "-I" (fix-for-mingw (ecl-include-directory)))
,@(split-program-options *cc-flags*)
,@(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))
`(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)
(setf *features* (delete :ecl-bytecmp *features*))

View file

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