[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.lisp

macros which remove dead code paths

Jim Newton

1/6/2016 9:08:00 AM

I have noticed, at least in SBCL, that sometimes the compiler notifies me about removing dead code.
The algorithm is pretty smart in warning me when it eliminates code which I wrote, but does not
bother me when it removes machine generated code.

If I have a macro which sometimes removes dead code, is there a way for me to play nicely with
the compiler.

E.g., I have a macro called reduced-typecase which does some transformations of the type specifications. Sometimes, it reduces the type to nil. In this case I leave the nil clause in so the compiler can warn
about removing dead code.

E.g.,
(reduced-typecase obj
((or A B) (F))
(A (G))
(B (H)))
-->
(typecase obj
((or A B) (F))
(nil (G))
(nil (H)))


However, sometimes, I eliminate cases altogether (for example if the first case it the t type. E.g.
(reduced-typecase obj
((or A (not A)) (F))
(t (G)))
-->
(progn obj (F))

In such a case I'd like the compiler to warn that dead code was removed, provided that code was written by me.

What is the correct way to handle this?
15 Answers

Norbert_Paul

1/6/2016 3:37:00 PM

0

Jim Newton wrote:
> I have noticed, at least in SBCL, that sometimes the compiler notifies me about removing dead code.
> The algorithm is pretty smart in warning me when it eliminates code which I wrote, but does not
> bother me when it removes machine generated code.
>
> If I have a macro which sometimes removes dead code, is there a way for me to play nicely with
> the compiler.
>
> E.g., I have a macro called reduced-typecase which does some transformations of the type specifications. Sometimes, it reduces the type to nil. In this case I leave the nil clause in so the compiler can warn
> about removing dead code.
>
> E.g.,
> (reduced-typecase obj
> ((or A B) (F))
> (A (G))
> (B (H)))
> -->
> (typecase obj
> ((or A B) (F))
> (nil (G))
> (nil (H)))
>
>
> However, sometimes, I eliminate cases altogether (for example if the first case it the t type. E.g.
> (reduced-typecase obj
> ((or A (not A)) (F))
> (t (G)))
> -->
> (progn obj (F))
>
> In such a case I'd like the compiler to warn that dead code was removed, provided that code was written by me.
>
> What is the correct way to handle this?

Maybe DEFINE-COMPILER-MACRO is what you are looking for:
http://clhs.lisp.se/Body/m_...

Note the Notes at the end.

Pascal J. Bourguignon

1/6/2016 7:02:00 PM

0

Norbert_Paul <norbertpauls_spambin@yahoo.com> writes:

> Jim Newton wrote:
>> If I have a macro which sometimes removes dead code, is there a way
>> for me to play nicely with
>> the compiler.
>> [â?¦]
>> In such a case I'd like the compiler to warn that dead code was
>> removed, provided that code was written by me.
>>
>> What is the correct way to handle this?
>
> Maybe DEFINE-COMPILER-MACRO is what you are looking for:
> http://clhs.lisp.se/Body/m_...
>
> Note the Notes at the end.

Compiler-macros are for functions, they have nothing to do with macros.

--
__Pascal Bourguignon__ http://www.informat...
â??The factory of the future will have only two employees, a man and a
dog. The man will be there to feed the dog. The dog will be there to
keep the man from touching the equipment.� -- Carl Bass CEO Autodesk

Kaz Kylheku

1/6/2016 7:46:00 PM

0

On 2016-01-06, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> Norbert_Paul <norbertpauls_spambin@yahoo.com> writes:
>
>> Jim Newton wrote:
>>> If I have a macro which sometimes removes dead code, is there a way
>>> for me to play nicely with
>>> the compiler.
>>> [â?¦]
>>> In such a case I'd like the compiler to warn that dead code was
>>> removed, provided that code was written by me.
>>>
>>> What is the correct way to handle this?
>>
>> Maybe DEFINE-COMPILER-MACRO is what you are looking for:
>> http://clhs.lisp.se/Body/m_...
>>
>> Note the Notes at the end.
>
> Compiler-macros are for functions, they have nothing to do with macros.

"Nothing" is overstated here. They have a lot to do with regular macros
conceptually; only, they are separated from them in Common Lisp.

However, it is possible to have a Lisp dialect in which compiler macros
are implemented as regular macros. You simply allow a symbol to
simultaneously have a function and macro binding (at least in the global
environment), and ensure that your code walker terminates the repeated
expansion of a macro when the expander returns a value EQ to the input
form. (Which is probably a good idea to do anyway: when macro expansion
reaches a fixed point under EQ, maybe that's a good signal to bail!)

Under these rules, the macro can then emit a call to the same-named
function. If that return value isn't just the input form, then the macro
is called again; it then somehow recognizes that no further expansion is
required, and returns the whole form. At that point, its return value
is EQ to the form, and so expansion terminates.

In such a dialect, you can have macros and functions of the same name
which behave differently. For instance you can have an AND macro which
performs the usual short circuit evaluation: it expands into code which
makes no use of the AND function. That function happens to exist
simultaneously, and allows applicative use like (apply #'and args ...).
This will cut n00b calls to your Lisp support hotline by at least 55%.
No more, "why can't I funcall the AND function?" You just darned *can*,
and that's it!

You can still implement the requirement that compiler macros are optional
when not compiling. This can be achieved as follows: you can have some
special variable like *compiling* which is bound to T when macros are
expanded in the context of compilation. You can then write macros which react
to this flag being true by bypassing their expansion logic and returning
the &whole form. As a bonus, you retain the freedom to to have your
macros ignore the flag and transform anyway, if you want.
If you're so inclined, you could write a CL-compatible
DEFINE-COMPILER-MACRO macro which adds this *compiling* check.

Barry Margolin

1/6/2016 8:17:00 PM

0

In article <20160106112213.398@kylheku.com>,
Kaz Kylheku <kaz@kylheku.com> wrote:

> On 2016-01-06, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
> > Norbert_Paul <norbertpauls_spambin@yahoo.com> writes:
> >
> >> Jim Newton wrote:
> >>> If I have a macro which sometimes removes dead code, is there a way
> >>> for me to play nicely with
> >>> the compiler.
> >>> [â?¦]
> >>> In such a case I'd like the compiler to warn that dead code was
> >>> removed, provided that code was written by me.
> >>>
> >>> What is the correct way to handle this?
> >>
> >> Maybe DEFINE-COMPILER-MACRO is what you are looking for:
> >> http://clhs.lisp.se/Body/m_...
> >>
> >> Note the Notes at the end.
> >
> > Compiler-macros are for functions, they have nothing to do with macros.
>
> "Nothing" is overstated here. They have a lot to do with regular macros
> conceptually; only, they are separated from them in Common Lisp.

I think his point is that compiler-macros can only be used to optimize
ordinary functions. You can't use them where you need to prevent
argument evaluation, which is one of the common reasons to define a
macro.

E.g. you can't implement TYPECASE as a compiler-macro, it has to be a
real macro.

--
Barry Margolin, barmar@alum.mit.edu
Arlington, MA
*** PLEASE post questions in newsgroups, not directly to me ***

Kaz Kylheku

1/6/2016 8:49:00 PM

0

On 2016-01-06, Barry Margolin <barmar@alum.mit.edu> wrote:
> In article <20160106112213.398@kylheku.com>,
> Kaz Kylheku <kaz@kylheku.com> wrote:
>
>> On 2016-01-06, Pascal J. Bourguignon <pjb@informatimago.com> wrote:
>> > Norbert_Paul <norbertpauls_spambin@yahoo.com> writes:
>> >
>> >> Jim Newton wrote:
>> >>> If I have a macro which sometimes removes dead code, is there a way
>> >>> for me to play nicely with
>> >>> the compiler.
>> >>> [â?¦]
>> >>> In such a case I'd like the compiler to warn that dead code was
>> >>> removed, provided that code was written by me.
>> >>>
>> >>> What is the correct way to handle this?
>> >>
>> >> Maybe DEFINE-COMPILER-MACRO is what you are looking for:
>> >> http://clhs.lisp.se/Body/m_...
>> >>
>> >> Note the Notes at the end.
>> >
>> > Compiler-macros are for functions, they have nothing to do with macros.
>>
>> "Nothing" is overstated here. They have a lot to do with regular macros
>> conceptually; only, they are separated from them in Common Lisp.
>
> I think his point is that compiler-macros can only be used to optimize
> ordinary functions.

I rather think the point is that Lisp refers to Common Lisp, and the way
things are in CL, and that's it.

> You can't use them where you need to prevent
> argument evaluation, which is one of the common reasons to define a

Yes you can.

Given

(defun mult (a b) (* a b))

we can write a compiler macro which boils down to a constant zero if
either A or B is a constant zero. The opposite operand of the constant
zero is not evaluated, even if it is a non-constant form with side
effects. This of course breaks the semantics of the function call, but
if that is clearly understood, so what? That's one of the nice things of
this user-defined optimization feature: I don't have some compiler
writer or standards committee member dictating to me that I shouldn't
even *want* such an optimization.

> macro.

> E.g. you can't implement TYPECASE as a compiler-macro, it has to be a
> real macro.

This is only because a (typecase ...) form may not be relied upon
to always expand. It can be left unexpanded, in which case a typecase
function is required for the form to be valid.

If there was a requirement that, given a compiler macro typecase,
the call (typecase ...) must always be expanded prior to evaluation,
then of course you could get it to do whatever you want, and
to work in the complete absence of a typecase function.

Alberto Riva

1/6/2016 10:45:00 PM

0

On 01/06/2016 04:08 AM, Jim Newton wrote:
> However, sometimes, I eliminate cases altogether (for example if the first case it the t type. E.g.
> (reduced-typecase obj
> ((or A (not A)) (F))
> (t (G)))
> -->
> (progn obj (F))
>
> In such a case I'd like the compiler to warn that dead code was removed, provided that code was written by me.
>
> What is the correct way to handle this?

In this case, if I understand correctly, *your* macro is doing the
removal, so your macro should print a warning when it does that. If your
macro removes the code, the compiler never sees it, so it has no way of
knowing that it was removed.

Alberto

Alberto Riva

1/6/2016 10:52:00 PM

0

On 01/06/2016 05:44 PM, Alberto Riva wrote:
> On 01/06/2016 04:08 AM, Jim Newton wrote:
>> However, sometimes, I eliminate cases altogether (for example if the
>> first case it the t type. E.g.
>> (reduced-typecase obj
>> ((or A (not A)) (F))
>> (t (G)))
>> -->
>> (progn obj (F))
>>
>> In such a case I'd like the compiler to warn that dead code was
>> removed, provided that code was written by me.
>>
>> What is the correct way to handle this?
>
> In this case, if I understand correctly, *your* macro is doing the
> removal, so your macro should print a warning when it does that. If your
> macro removes the code, the compiler never sees it, so it has no way of
> knowing that it was removed.

For example, let's say you want to write a macro that wraps its
arguments in a PROGN, eliminating those that are NIL (it's a silly
example because they would not have any effect anyway, except in the
last position, but it's just to show what I mean).

You can write:

(defmacro cleaned-progn (&rest forms)
(let* ((removed 0)
(valid (loop for f in forms
if f collect f
else do (incf removed))))
(warn "~a empty forms removed." removed)
`(progn ,@valid)))
CLEANED-PROGN

> (macroexpand '(cleaned-progn (do-a) nil (do-b) nil (do-c)))
Warning: 2 empty forms removed.
(PROGN (DO-A) (DO-B) (DO-C))
T


Alberto

Jim Newton

1/7/2016 8:25:00 AM

0

> ... ensure that your code walker terminates the repeated
> expansion of a macro when the expander returns a value EQ to the input
> form. (Which is probably a good idea to do anyway: when macro expansion
> reaches a fixed point under EQ, maybe that's a good signal to bail!)
>

In reading the spec, in some places it seems the compiler macro should return NIL
to tell the system NOT to transform the code, but simply compile a call to the function.
Other places seem to indicate that a compiler macro should use the &whole declaration
to return the given form if it decides to make no transformation.

Which one is correct? Or do both have the same semantic?

Jim Newton

1/7/2016 8:30:00 AM

0


> > What is the correct way to handle this?
>
> In this case, if I understand correctly, *your* macro is doing the
> removal, so your macro should print a warning when it does that. If your
> macro removes the code, the compiler never sees it, so it has no way of
> knowing that it was removed.
>
> Alberto

Hi Alberto, there are two problems that arise if my macro issues the warning.

1) if my macro prints a warning, the user will see the warning
EVEN in the case that it is removing machine generated code. I'd like to play nicely
with the compiler and let the compiler figure out whether to issue the warning. My gut
feel is that what I'm requesting is impossible.

2) when the compiler (SBCL) issues the warning about dead code, slime highlights the cod
in question. If my macro issues such a warning, it needs to look the same in slime as if SBCL had done it.

Alberto Riva

1/7/2016 3:17:00 PM

0

On 01/07/2016 03:30 AM, Jim Newton wrote:
>
>>> What is the correct way to handle this?
>>
>> In this case, if I understand correctly, *your* macro is doing the
>> removal, so your macro should print a warning when it does that. If your
>> macro removes the code, the compiler never sees it, so it has no way of
>> knowing that it was removed.
>>
>> Alberto
>
> Hi Alberto, there are two problems that arise if my macro issues the warning.
>
> 1) if my macro prints a warning, the user will see the warning
> EVEN in the case that it is removing machine generated code. I'd like to play nicely
> with the compiler and let the compiler figure out whether to issue the warning. My gut
> feel is that what I'm requesting is impossible.

But the message is printed when the macro is expanded, ie at compilation
time, so the user won't see it (I assume that by "user" you mean whoever
runs your code, while you're the one who compiles it).

> 2) when the compiler (SBCL) issues the warning about dead code, slime highlights the cod
> in question. If my macro issues such a warning, it needs to look the same in slime as if SBCL had done it.

Sorry, I don't use SBCL so I don't know how that works...

Alberto