diff --git a/CHANGELOG b/CHANGELOG index 7937d1a4a..1ec995843 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -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 diff --git a/src/cmp/cmpglobals.lsp b/src/cmp/cmpglobals.lsp index 60b86cca2..36bcaa56f 100644 --- a/src/cmp/cmpglobals.lsp +++ b/src/cmp/cmpglobals.lsp @@ -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. ;;; diff --git a/src/cmp/cmpmain.lsp b/src/cmp/cmpmain.lsp index dd2a4be40..431a3e0ab 100755 --- a/src/cmp/cmpmain.lsp +++ b/src/cmp/cmpmain.lsp @@ -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*)) diff --git a/src/h/ecl-cmp.h b/src/h/ecl-cmp.h index 3dda31e73..13beb47d4 100755 --- a/src/h/ecl-cmp.h +++ b/src/h/ecl-cmp.h @@ -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 */