Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

NOTINLINE

35 views
Skip to first unread message

Spiros Bousbouras

unread,
Apr 1, 2022, 12:21:18 PM4/1/22
to
The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ? Are there
some things which are guaranteed to work if a function has been declared
NOTINLINE but may or may not work otherwise ? For example , assuming no
declarations for the following functions , is the following guaranteed to
work as appears in the output below ?

==============
* (defun foo1 () 'foo1)

FOO1
* (defun foo2 () 'foo2)

FOO2
* (defun foo3 () 'foo3)

FOO3
* (foo3)

FOO3
* (setf (symbol-function 'foo3) (function foo1))

#<FUNCTION FOO1>
* (foo3)

FOO1
* (setf (symbol-function 'foo3) (function foo2))

#<FUNCTION FOO2>
* (foo3)

FOO2
==============

Might any declarations of INLINE affect the output ?

--
To some it is Napoleon, to some it is a philosophical struggle, to me it is
allegro con brio.
Arturo Toscanini
http://www.beethovenseroica.com/Pg3_anal/analmenu.htm

Jeff Barnett

unread,
Apr 1, 2022, 10:50:12 PM4/1/22
to
On 4/1/2022 10:21 AM, Spiros Bousbouras wrote:
> The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ? Are there
> some things which are guaranteed to work if a function has been declared
> NOTINLINE but may or may not work otherwise ? For example , assuming no
> declarations for the following functions , is the following guaranteed to
> work as appears in the output below ?

Consider a recursive function ....
--
Jeff Barnett

Spiros Bousbouras

unread,
Apr 1, 2022, 11:38:02 PM4/1/22
to
I don't see how a recursive function explains why NOTINLINE cannot be
ignored. It gives one explanation as to why INLINE can be ignored but this
wasn't the gist of my question. Recursive functions aside , INLINE can be
ignored simply to remove the burden from the compiler writers to add any
optimisations for it. It's the same reason why inline declarations can be
ignored in C .

Jeff Barnett

unread,
Apr 2, 2022, 12:42:27 AM4/2/22
to
If the compiler is not allowed to ignore a NOTINLINE that you have
proclaimed (or whatever) for a recursive function, how many expansions
should the compiler do before giving up and signalling an error or must
it wait until either disk, memory, or address space has been exhausted?

There are also many ways that an innocent short looking function can
expand into a huge number of operations - inline expansion of functions
it applies (to many levels), macro usages that make make a lot out of a
little, and so on. Etc.

I will say on further reflection that this appears to be one of the few
places that the spec tries to keep system implementers from shooting
themselves (and their users) in the foot. Doesn't sound all that Lisp
like does it?
--
Jeff Barnett

Helmut Eller

unread,
Apr 2, 2022, 5:43:01 AM4/2/22
to
On Fri, Apr 01 2022, Spiros Bousbouras wrote:

> The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ? Are there
> some things which are guaranteed to work if a function has been declared
> NOTINLINE but may or may not work otherwise ?

This may have something to do with file compilation. I think a CL
compiler is allowed to inline calls to (global) functions defined within
the same file. The NOTINLINE declaration could be used to enforce
certain indirections. E.g., it may be useful when you want to redefine
a function.

Helmut

Zyni Moë

unread,
Apr 2, 2022, 7:19:53 AM4/2/22
to
Spiros Bousbouras <spi...@gmail.com> wrote:
> The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ? Are there
> some things which are guaranteed to work if a function has been declared
> NOTINLINE but may or may not work otherwise ?

Yes. As one example if you wish to memoize a function is generally
necessary that recursive calls to it, including tail calls, are not
optimized away: they must be full calls which look up the symbol-function
and call the function they find there, because it is that function which is
doing the memoizing.

Consider this:

(defun obvious-fibonacci (n)
... two self-calls to obvious-fibonacci ...)

Compiler 'optimizes' these self-calls, obvious-fibonacci is now exponential
time. Now we do (memoize 'obvious-fibonacci). Oh dear, is still
exponential because only one call (at most) is ever memoized. Instead do

(declaim (notinline obvious-fibonacci))
(defun obvious-fibonacci ...)
(memoize 'obvious-fibonacci)

And now obvious-fibonacci is fast.


--
the small snake

Spiros Bousbouras

unread,
Apr 2, 2022, 11:47:43 AM4/2/22
to
On Fri, 1 Apr 2022 22:42:15 -0600
Jeff Barnett <j...@notatt.com> wrote:
> On 4/1/2022 9:37 PM, Spiros Bousbouras wrote:
> > On Fri, 1 Apr 2022 20:50:01 -0600
> > Jeff Barnett <j...@notatt.com> wrote:
> >> On 4/1/2022 10:21 AM, Spiros Bousbouras wrote:
> >>> The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> >>> ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ? Are there
> >>> some things which are guaranteed to work if a function has been declared
> >>> NOTINLINE but may or may not work otherwise ? For example , assuming no
> >>> declarations for the following functions , is the following guaranteed to
> >>> work as appears in the output below ?
> >>
> >> Consider a recursive function ....
> >
> > I don't see how a recursive function explains why NOTINLINE cannot be
> > ignored. It gives one explanation as to why INLINE can be ignored but this
> > wasn't the gist of my question. Recursive functions aside , INLINE can be
> > ignored simply to remove the burden from the compiler writers to add any
> > optimisations for it. It's the same reason why inline declarations can be
> > ignored in C .
>
> If the compiler is not allowed to ignore a NOTINLINE that you have
> proclaimed (or whatever) for a recursive function, how many expansions
> should the compiler do before giving up and signalling an error or must
> it wait until either disk, memory, or address space has been exhausted?

Not even 1 expansion because it would be pointless. That's a quality of
implementation issue and has nothing to do with any NOTINLINE declaration.
Even if a function has been declared INLINE , a compiler is still not allowed
to fail to compile it because it entered into an infinite recursion by trying
to expand it inline. And it's actually easy to detect that a function
directly calls itself ; if the call is not direct then inline expansion is
not relevant anyway.

> There are also many ways that an innocent short looking function can
> expand into a huge number of operations - inline expansion of functions
> it applies (to many levels), macro usages that make make a lot out of a
> little, and so on. Etc.

So if the programmer believes that a function will be called in many places ,
he declares the function NOTINLINE to reduce the size of the generated code.
This seems like a valid reason. It also raises the question whether , in the
absence of any [NOT]INLINE declarations , the compiler is allowed to expand
[some] calls to the function inline.

> I will say on further reflection that this appears to be one of the few
> places that the spec tries to keep system implementers from shooting
> themselves (and their users) in the foot. Doesn't sound all that Lisp
> like does it?

I don't think it's about users not shooting themselves in the foot. Declaring
a function NOTINLINE strikes me as a kind of esoteric thing to do so the
programmer must already have her reasons.

--
vlaho.ninja/prog

Steve G

unread,
Apr 3, 2022, 10:14:33 AM4/3/22
to
Spiros Bousbouras <spi...@gmail.com> writes:

> The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ?

It is a programming thing. I usually use it with interfaces so that if
the code changes any dependent software will not have to be recompiled.

Ex...

;;;; Termcap Interface
(declaim (notinline tgetstr tgetnum tgetstr))

(defun tgetflag (cap)
"Return value of termcap boolean capability <CAP>."
(declare (type simple-string cap))
(get-sequence cap))

(defun tgetnum (cap)
"Return value of boolean capability <CAP>."
(declare (type simple-string cap))
(get-sequence cap))

(defun tgetstr (cap)
"Return a string for capability <CAP>."
(declare (type simple-string cap))
(get-sequence cap))


If these functions where inlined then every time I recompile the file,
the entire program must be recompiled. This is similar to static
libraries vs. dynamic libraries. On unix that would be `.a' and `.so' .
On windows it would be a DLL.

I think most people can agree that static libraries can lead to some
very agrevating problems.


> Are there
> some things which are guaranteed to work if a function has been declared
> NOTINLINE but may or may not work otherwise ?

When debugging code :)


Seriousy, gcc has this problem like nothing else. When compiling with
GCC if you want to debug use -ggdb and an optimization of -Og.

This is a new feature - a very good one, like -fverbose-asm.


> declarations for the following functions , is the following guaranteed to
> work as appears in the output below ?
>
> ==============
> * (defun foo1 () 'foo1)
>
> FOO1
> * (defun foo2 () 'foo2)
>
> FOO2
> * (defun foo3 () 'foo3)
>
> FOO3
> * (foo3)
>
> FOO3
> * (setf (symbol-function 'foo3) (function foo1))


No. I believe you are misunderstanding symbol-function. Symbol-function
is like using the C preprocessor but at run time.


;;;; Globals and symbol-macros

(defstruct (screen (:copier nil) :named)
(namestring "Standard Screen" :type simple-string :read-only t)
;; [ ... ]

;; cursor position movement
(cur-x 0 :type (unsigned-byte 16))
(cur-y 0 :type (unsigned-byte 16))
(max-x 0 :type (unsigned-byte 16))
(max-y 0 :type (unsigned-byte 16)))


(defvar *current-screen* (make-screen)
"The current screen object.")

(defun current-screen ()
"Return the current screen object. This function is never called directly."
(the screen *current-screen*))

(define-symbol-macro *LINES* (screen-max-x (current-screen)))
;; "Number of rows for the current screen."

(define-symbol-macro *COLUMNS* (screen-max-y (current-screen)))
;; "Number of columns for the current screen."


The symbol-macro *COLUMNS* can be used like a global variable, but the
value is computed at runtime. Very cool feature.

> #<FUNCTION FOO1>
> * (foo3)
>
> FOO1
> * (setf (symbol-function 'foo3) (function foo2))
>
> #<FUNCTION FOO2>
> * (foo3)
>
> FOO2
> ==============
>
> Might any declarations of INLINE affect the output ?

When do you need the values? At runtime or compile time? I try not to
ever use global variables - I love the symbol-macro.

hope this helps some. if not please just ask away...

Spiros Bousbouras

unread,
Apr 3, 2022, 11:54:51 AM4/3/22
to
On Sun, 03 Apr 2022 10:13:42 -0400
Steve G <Sgoned...@gmail.com> wrote:
> Spiros Bousbouras <spi...@gmail.com> writes:
>
> > The CLHS page for INLINE and NOTINLINE say that a compiler "is free to
> > ignore" INLINE but "is not free to ignore" NOTINLINE .Why is that ?
>
> It is a programming thing. I usually use it with interfaces so that if
> the code changes any dependent software will not have to be recompiled.

Ok , that makes sense.
The examples below are about symbol macros. But I asked about SYMBOL-FUNCTION
, a completely different thing.
I don't have a specific usage in mind , I was just experimenting. But
lets say at runtime.

Zyni Moë

unread,
Apr 3, 2022, 2:58:27 PM4/3/22
to
Steve G <Sgoned...@gmail.com> wrote:

> If these functions where inlined then every time I recompile the file,
> the entire program must be recompiled. This is similar to static
> libraries vs. dynamic libraries. On unix that would be `.a' and `.so' .
> On windows it would be a DLL.
>

Much more important, because it happens even when you do not explicitly
declare functions inline, are these two paragraphs from 3.2.2.3:


- Within a function named F, the compiler may (but is not required to)
assume that an apparent recursive call to a function named F refers to the
same definition of F, unless that function has been declared notinline. The
consequences of redefining such a recursively defined function F while it
is executing are undefined.

- A call within a file to a named function that is defined in the same file
refers to that function, unless that function has been declared notinline.
The consequences are unspecified if functions are redefined individually at
run time or multiply defined in the same file.

These two paragraphs explain why notinline cannot be ignored.

--
the small snake

steve gonedes

unread,
Apr 5, 2022, 3:50:52 PM4/5/22
to
On Sunday, April 3, 2022 at 2:58:27 PM UTC-4, Zyni Moë wrote:
> Steve G <Sgoned...@gmail.com> wrote:
>
> > If these functions where inlined then every time I recompile the file,
> > the entire program must be recompiled. This is similar to static
> > libraries vs. dynamic libraries. On unix that would be `.a' and `.so' .
> > On windows it would be a DLL.
> >
> Much more important, because it happens even when you do not explicitly
> declare functions inline, are these two paragraphs from 3.2.2.3:
>
>
> - Within a function named F, the compiler may (but is not required to)
> assume that an apparent recursive call to a function named F refers to the
> same definition of F, unless that function has been declared notinline. The
> consequences of redefining such a recursively defined function F while it
> is executing are undefined.
>

can you explain this mathematically or using pseudo-code?

> - A call within a file to a named function that is defined in the same file
> refers to that function, unless that function has been declared notinline.
> The consequences are unspecified if functions are redefined individually at
> run time or multiply defined in the same file.
>

A call within a file?




> --
> the small snake

steve

unread,
Apr 5, 2022, 3:56:08 PM4/5/22
to
at runtime I would use a symbol macro not function slot.



Zyni Moë

unread,
Apr 5, 2022, 4:48:04 PM4/5/22
to
steve gonedes <sgoned...@gmail.com> wrote:

>> - Within a function named F, the compiler may (but is not required to)
>> assume that an apparent recursive call to a function named F refers to the
>> same definition of F, unless that function has been declared notinline. The
>> consequences of redefining such a recursively defined function F while it
>> is executing are undefined.
>>
>
> can you explain this mathematically or using pseudo-code?

This is text from the standard. However what it means is that in a case
like

... no notinline drclaration for f ...

(defun f (...)
... no notinline declaration for f or intervening labels or flet for f
...
(f ...)
...)

The compiler may assume that the inner call to f refers to the outer
function and (for instance) avoid doing a full call. If there is a
notinline declaration (either free or bound) for f it may not make such
assumption and must do full call, so (f ...) is more or less (funcall
(fdefinition 'f) ...).

>
>> - A call within a file to a named function that is defined in the same file
>> refers to that function, unless that function has been declared notinline.
>> The consequences are unspecified if functions are redefined individually at
>> run time or multiply defined in the same file.
>>
>
> A call within a file?
>

A call made by code which occurs within the same file where the function is
defined, yes.

--
the small snake

Tom Russ

unread,
Apr 5, 2022, 4:50:57 PM4/5/22
to
On Tuesday, April 5, 2022 at 12:50:52 PM UTC-7, sgoned...@gmail.com wrote:
> On Sunday, April 3, 2022 at 2:58:27 PM UTC-4, Zyni Moë wrote:
...
> > - Within a function named F, the compiler may (but is not required to)
> > assume that an apparent recursive call to a function named F refers to the
> > same definition of F, unless that function has been declared notinline. The
> > consequences of redefining such a recursively defined function F while it
> > is executing are undefined.
> >
> can you explain this mathematically or using pseudo-code?

I doubt it can be explained mathematically, as there is no real mathematical
notion of the redefinition of functions during their execution. But here is an
example in lisp:

.(defun f (x)
. (cond ((<= x 0) -10)
. ((eql x 10)
. (setf (symbol-function 'f)
. #'(lambda (y)
. (cond ((<= y 0)
. -1)
. (t (print y) (f (1- y))))))
. (f (1- x)))
. (t (print x) (f (- x 5)))))

Try it with something like (f 30).

> > - A call within a file to a named function that is defined in the same file
> > refers to that function, unless that function has been declared notinline.
> > The consequences are unspecified if functions are redefined individually at
> > run time or multiply defined in the same file.
> >
> A call within a file?

At runtime, calling a function that is defined in the same file or perhaps more
generally in the same "translation unit". So if you define F and G in the same
file, and F calls G, the compiler is allowed to in-line G in the body of F.

Zyni Moë

unread,
Apr 5, 2022, 5:29:48 PM4/5/22
to
Tom Russ <tar...@google.com> wrote:
> the compiler is allowed to in-line G in the body of F.
>

This is again not to do with inlining. Is rather to do with being allowed
to assume that definition of G is definition in that file, so call to G
does not need to go through function cell of G, can assume lambda list of G
etc. Whether G is inlined is other issue.


--
the small snake

<Stevie G>

unread,
Apr 5, 2022, 7:19:51 PM4/5/22
to
Zyni Moë <no_e...@invalid.invalid> writes:

> steve gonedes <sgoned...@gmail.com> wrote:
>
>>> - Within a function named F, the compiler may (but is not required to)
>>> assume that an apparent recursive call to a function named F refers to the
>>> same definition of F, unless that function has been declared notinline. The
>>> consequences of redefining such a recursively defined function F while it
>>> is executing are undefined.
>>>
>>
>> can you explain this mathematically or using pseudo-code?
>
> This is text from the standard. However what it means is that in a case
> like
>
> ... no notinline drclaration for f ...
>
> (defun f (...)
> ... no notinline declaration for f or intervening labels or flet for f
> ...
> (f ...)
> ...)


Thank You! Now I know where you are at...


> The compiler may assume that the inner call to f refers to the outer
> function and (for instance) avoid doing a full call. If there is a
> notinline declaration (either free or bound) for f it may not make such
> assumption and must do full call, so (f ...) is more or less (funcall
> (fdefinition 'f) ...).



yes, like flet or labels. I usually use declaim - not declare. never
thougt about it.


>>
>>> - A call within a file to a named function that is defined in the same file
>>> refers to that function, unless that function has been declared notinline.
>>> The consequences are unspecified if functions are redefined individually at
>>> run time or multiply defined in the same file.
>>>
>>
>> A call within a file?


Ahh. within scope.. my bad...

<Stevie G>

unread,
Apr 5, 2022, 7:22:19 PM4/5/22
to
like a compilation unit. My bad, I'm getting old, tire, and annoyinng.

Steve G

unread,
Apr 5, 2022, 7:47:50 PM4/5/22
to
"<Stevie G>" <Sgoned...@gmail.com> writes:

> Zyni Moë <no_e...@invalid.invalid> writes:
>
>> steve gonedes <sgoned...@gmail.com> wrote:
>>
>>>> - Within a function named F, the compiler may (but is not required to)
>>>> assume that an apparent recursive call to a function named F refers to the
>>>> same definition of F, unless that function has been declared notinline. The
>>>> consequences of redefining such a recursively defined function F while it
>>>> is executing are undefined.
>>>>
>>>
>>> can you explain this mathematically or using pseudo-code?


I feel a bit foolish - I just read the paper. yes, with compilation
unit. Something struct me... just ramblings..
0 new messages