Skip to content

Investigating compiler issues

R. Matthew Emerson edited this page May 21, 2024 · 2 revisions

Techniques for investigating compiler issues

Here are some ways to investigate what is happening when the compiler produces code that doesn't work as expected. They are not in any particular order.

The disassemble function

Using disassemble can sometimes be helpful. The disassembly can show, for example, whether declarations did anything, or whether constant-folding worked, and so forth.

On x86, a function is a specially-tagged vector. You can use ccl::function-to-function-vector to turn a function into a uvector that you can inspect with describe or whatever. The vector consists of binary instructions, a marker word (x8664::function-boundary-marker), and then the function's constants/immediates.

It can sometimes help to compile with speed 3 and safety 0 to get rid of safety checks so that it's easier to see the actual code of interest in the disassembly.

Remember compiler macros

Many function names have compiler macros. These are mostly defined in compiler/optimizers.lisp. Recall that you can inhibit compiler macros by using, for example, (declare (notinline integerp)). You can use ccl:compiler-macroexpand-1 and ccl:compiler-macroexpand to help debug compiler macros.

Examine generated acode

The compiler front end produces an intermediate representation called acode. It can sometimes be helpful to see that.

(in-package :ccl)
(let ((*nx-current-code-note* nil))
       (nx1-compile-lambda 'fact '(lambda (n) (if (<= n 1) 1 (* n (fact (1- n)))))))
=> #<AFUNC #x302000AEF19D>
(pprint (decomp-acode (afunc-acode *)))
=>
(LAMBDA-LIST (N)
 (IF (NUMCMP (IMMEDIATE :LE) (LEXICAL-REFERENCE N) 1)
     1
     (MUL2 (LEXICAL-REFERENCE N)
      (FUNCALL (IMMEDIATE FACT) (SUB2 (LEXICAL-REFERENCE N) 1)))))

acode transformation

Generally, acode trees will get walked and rewritten via rewrite-acode-form. See compiler/acode-rewrite.lisp for the rewrite functions. If you suspect something is going wrong in this area, you might want to make rewrite-acode-form a no-op.

Look at vinsns

The compiler backend turns the intermediate representation into a compiled function. Sometimes it can help to see what vinsns (virtual instructions) it is using.

On x86 you can do (setq ccl::*x86-debug-mask* 2) to tell the backend to print out the generated vinsns. (The value 2 is the value of (ash 1 x862-debug-vinsns-bit) in case you were wondering.)