4

An SO post containing a response by coredump shows how to apply a compiler policy to an ASDF system's component files:

(defsystem simple-system
  :serial t
  :around-compile (lambda (next)
                    (proclaim '(optimize (debug 3) 
                                         (safety 3)
                                         (debug 3)
                                         (speed 0)))
                    (funcall next))
  :components ((:module "src"
                        :components
                        (...))))

It also mentions that you can "shadow" individual files, but how would this work. It's confusing to me because next in the lambda expression is bound to a closure. Since I only need to apply the optimization to a couple of component files, how do you give those file names to :around-compile?

davypough
  • 1,847
  • 11
  • 21

1 Answers1

6

You can add :around-compile for a system, a module or a file.

More precisely, if you have a :file component like this:

(:file "a")

Then you can add:

(:file "a" :around-compile ...)

If you only want to apply optimizations to a given set of files, group them in a module. You can even set the module's pathanme to "" so that its files are in the same directories are the sibling components:

(:module #:MY-OPTIMIZED-FILES
         :depends-on (...)
         ;; SAME DIRECTORY
         :pathname ""
         :serial t
         :around-compile "my-meta-lib:around-compile"
         :components ((:file "a")
                      (:file "b")
                      (:file "c")
                      (:file "d")))

You cannot refer to a symbol if the system that defines it is not loaded, and in the case of an ASDF system, you can't declare dependencies without reading first the form that defines the system. So you need to use strings to refer to a symbol in another package.

When the system is processed, the string must refer to an existing symbol, so you need to have a different .asd file, for example simple-system.meta.asd, which defines the system "simple-system.meta". You add a dependency with :defsystem-depends-on to make sure the meta system is loaded before simple-system is processed.

That system could be for example:

(defsystem simple-system.meta
  :depends-on ("trivial-cltl2")
  :components ((:file "meta")))

The reason I am using trivial-cltl2 is to be able to introspect the declaration in the global environment and hopefully limit the effects of proclaim:

(defun my-meta-lib:around-compile (next)
  (let ((opt (trivial-cltl2:declaration-information 'optimize)))
    (proclaim '(optimize (debug 3) 
                         (safety 3)
                         (debug 3)
                         (speed 0)))
    (unwind-protect (funcall next)
      (proclaim (list* 'optimize opt)))))

As far as I know, proclaim modifies the global environment and it could affect the compilation of other files, that's why I prefer to restore the environment after compilation finishes.

SBCL has an experimental :policy option for with-compilation-unit that is made for this use case, the policy is modified in the dynamic extent of the macro:

(flet ((debug () (assoc 'debug (sb-cltl2:declaration-information 'optimize))))
  (list (debug)
        (with-compilation-unit (:policy '(optimize (debug 3)))
          (debug))
        (debug)))

 ; => ((DEBUG 1) (DEBUG 3) (DEBUG 1))
coredump
  • 37,664
  • 5
  • 43
  • 77
  • 1
    I've tried adding `:around-compile (lambda (next) (proclaim '(optimize (debug 3))) (funcall next))` for one middle component file, which works fine. But after the whole system is compiled and loaded, `(sb-ext:describe-compiler-policy)` says debug is still set to 3. Are the following component files being compiled with `(debug 3)` also? If so, this is not what I intended. – davypough Jan 06 '21 at 21:16
  • 2
    Yes, because I think proclaim sets the global compilation policy, once you set it keeps being set; that's why I tried to implement a more composable approach with `my-meta-lib:around-compile`, which grabs the current declaration for optimize, calls proclaim, then restores the previous declarations when unwinding. I did an edit today to fix the symbol vs. string problem I had originally, and it seems to work, the policy is applied only for the specified files – coredump Jan 06 '21 at 21:31
  • 2
    I also though about (locally (declare (optimize ...)) (funcall next)) but the hyperspec was not clear about whether or not this should work in the dynamic extent, I'll try to find more information about that – coredump Jan 06 '21 at 21:33
  • 2
    The docstring for WITH-COMPILATION-UNIT in SBCL says that it has a `:POLICY` option. This is an implementation-dependant option but this is the proper way to do it with SBCL. – coredump Jan 06 '21 at 21:36
  • 2
    Note the required SBCL module name seems to be `sb-cltl2`. – davypough Jan 17 '21 at 00:30
  • 1
    @davypough thanks, I tested that with trivial-cltl2 alread loaded so there was a nickname in place, I edited the answer – coredump Jan 17 '21 at 10:13