DEFUN-INLINE

define a potentially inlined function symbol and associated macro
Major Section:  EVENTS

You may be able to improve performance by replacing an event (defun f ...) with a corresponding event (defun-inline f ...), in order to encourage the host Lisp compiler to inline calls of f.

Example Form:
(defun-inline lng (x)
  (declare (xargs :guard (true-listp x)))
  (if (endp x) 0 (1+ (lng (cdr x)))))

General Form:
(defun-inline fn (var1 ... varn) doc-string dcl ... dcl body)
satisfying the same requirements as in the General Form for defun. The effect is to define a macro fn and a function fn$inline (i.e., a symbol in the same package as fn but whose symbol-name has the suffix "$INLINE", such that each call of fn expands to a call of the function symbol fn$inline on the same arguments. If doc-string is supplied, then it is associated with fn, not with fn$inline. Moreover, table events are generated that allow the use of fn in theory expressions to represent fn$inline and that cause any untranslated (user-level) call of fn$inline to be printed as the corresponding call of fn.

A form (defun-inline f ...) actually defines a function named f$inline and a corresponding macro named f whose calls expand to calls of f$inline, while providing the illusion that there is just the ``function'' f. For example, the Example Form above macroexpands in one step to the following form.

(progn (defmacro lng (non-stobj-var0)
         (list 'lng$inline non-stobj-var0))
       (add-macro-fn lng lng$inline)
       (defun lng$inline (x)
         (declare (xargs :guard (true-listp x)))
         (if (endp x) 0 (1+ (lng (cdr x))))))
Note that the above call of add-macro-fn generates the aforementioned two table events (see add-macro-fn), which provide the illusion that we are just defining a function lng, as you can see in the following log: lng appears rather than lng$inline.
ACL2 !>(set-gag-mode nil)
<state>
ACL2 !>(thm (equal (lng (append x y))
                   (+ (lng x) (lng y)))
            :hints (("Goal" :in-theory (enable lng))))

[.. output omitted ..]

Subgoal *1/2
(IMPLIES (AND (NOT (ENDP X))
              (EQUAL (LNG (APPEND (CDR X) Y))
                     (+ (LNG (CDR X)) (LNG Y))))
         (EQUAL (LNG (APPEND X Y))
                (+ (LNG X) (LNG Y)))).

[.. output omitted ..]

Under the hood, ACL2 arranges that every function symbol with suffix "$INLINE" is presented to the compiler as one whose calls we would prefer to inline. Technically: the Common Lisp form (declaim (inline f$inline)) is generated for a function symbol f$inline before that symbol's definition is submitted. However, the Common Lisp spec explicitly avoids requiring that the compiler respect this declaim form. Fortunately, Common Lisp implementations often do respect it.

Also see defund-inline, see defun-notinline, and see defund-notinline.

Remarks.

(1) None of these macros (including defun-inline) is supported for use inside a mutual-recursion.

(2) Every function symbol defined in ACL2 whose symbol-name has the suffix "$INLINE" is proclaimed to be inline; similarly for "$NOTINLINE" and notinline.

(3) No special treatment for inlining (or notinlining) is given for function symbols locally defined by flet, with two exceptions: when explicitly declared inline or notinline by the flet form, and for symbols discussed in (1) and (2) above that, at some point in the current ACL2 session, were defined as function symbols in ACL2 (even if not currently defined because of undoing or being local).

(4) The function symbol actually being defined by (defun-inline foo ...) is foo$inline. As mentioned above, one can be oblivious to this fact when writing theory expressions or perusing prover output. However, for other purposes (for example, verify-guards and defabsstobj :exports) you will need to supply the name of the function symbol rather than the name of the macro; e.g., for the above form (defun-inline foo ...), you may subsequently issue the event (verify-guards foo$inline) but not (verify-guards foo).

(5) Obscure Remark. Suppose that you certify a book with compilation (the default) in one host Lisp, saving the expansion file. Suppose that you then compile in another host Lisp by using include-book with argument :load-compiled-file :comp. Then in subsequent sessions, including that book with the second host Lisp will not result in any inline or notinline behavior for functions defined in the book. This may be fixed in a future release if someone complains.