[RFC] add Function Attribute to disable optimization

This thread is about defining an attribute to disable optimization on an
individual function.

Please let's not hijack it into a discussion of 'volatile'?

Thanks.
--paulr

Hi Nick,

From: Nick Lewycky <nicholas@mxc.ca>
> This proposal is to create a new function-level attribute which would

tell

> the compiler to not to perform any optimizing transformations on the
> specified function.

What about module passes? Do you want to disable all module passes in a
TU which contains a single one of these? I'll be unhappy if we need to
litter checks throughout the module passes that determine whether a
given instruction is inside an unoptimizable function or not. Saying
that module passes are exempt from checking the 'noopt' attribute is
fine to me, but then somebody needs to know how to module passes (and
users may be surprised to discover that adding such an annotation to one

function will cause seemingly-unrelated functions to become less

optimized).

Right, module passes are a difficult case.
I understand your point. I think ignoring the `noopt' attribute (or
whatever we want to call it) may be the best approach in this case: it
avoid the problems you describe but should still be sufficient for the
purposes we care about. I am currently studying the module passes in more
details to be certain about this.

Thanks for the useful feedback,
Andrea Di Biagio
SN Systems - Sony Computer Entertainment Group

> The use-case is to be able to selectively disable optimizations when
> debugging a small number of functions in a compilation unit to provide

an

> -O0-like quality of debugging in cases where compiling the whole unit

at

> anything less than full optimization would make the program run too
> slowly. A useful secondary-effect of this feature would be to allow

users

> to temporarily work-around optimization bugs in LLVM without having to
> reduce the optimization level for the whole compilation unit, however

we

> do not consider this the most important use-case.
>
> Our suggestion for the name for this attribute is "optnone" which

seems to

> be in keeping with the existing "optsize" attribute, although it could
> equally be called "noopt" or something else entirely. It would be

exposed

> to Clang users through __attribute__((optnone)) or [[optnone]].
>
> I would like to discuss this proposal with the rest of the community

to

> share opinions and have feedback on this.
>
> ===================================================
> Interactions with the existing function attributes:
>
> LLVM allows to decorate functions with 'noinline', alwaysinline' and
> 'inlinehint'. We think that it makes sense for 'optnone' to

implicitly

> imply 'noinline' (at least from a user's point of view) and therefore
> 'optnone' should be considered incompatible with 'alwaysinline' and
> 'inlinehint'.
>
> Example:
> __attribute__((optnone, always_inline))
> void foo() { ... }
>
> In this case we could make 'optnone' override 'alwaysinline'. The

effect

> would be that 'alwaysinline' wouldn't appear in the IR if 'optnone' is
> specified.
>
> Under the assumption that 'optnone' implies 'noinline', other things

that

> should be taken into account are:
> 1) functions marked as 'optnone' should never be considered as

potential

> candidates for inlining;
> 2) the inliner shouldn't try to inline a function if the call site is

in a

> 'optnone' function.
>
> Point 1 can be easily achieved by simply pushing attribute 'noinline'

on

> the list of function attributes if 'optnone' is used.
> point 2 however would probably require to teach the Inliner about
> 'optnone' and how to deal with it.
>
> As in the case of 'alwaysinline' and 'inlinehint', I think 'optnone'
> should also override 'optsize'/'minsize'.
>
> Last (but not least), implementing 'optnone' would still require

changes

> in how optimizations are run on functions. This last part is probably

the

> hardest part since the current optimizer does not allow the level of
> flexibility required by 'optnone'. It seems it would either require

some

> modifications to the Pass Manager or we would have to make individual
> passes aware of the attribute. Neither of these solutions seem
> particularly attractive to me, so I'm open to any suggestions!
>
> Thanks,
> Andrea Di Biagio
> SN Systems - Sony Computer Entertainment Group
>
>
> **********************************************************************
> This email and any files transmitted with it are confidential and

intended

> solely for the use of the individual or entity to whom they are

addressed.

> If you have received this email in error please notify

postmaster@scee.net

> This footnote also confirms that this email message has been checked

for

So..
I have investigated more on how a new function attribute to disable
optimization on a per-function basis could be implemented.
At the current state, with the lack of specific support from the pass
managers I found two big problems when trying to implement a prototype
implementation of the new attribute.

Here are the problems found:
1) It is not safe to disable some transform passes in the backend.
It looks like there are some unwritten dependences between passes and
disrupting the sequence of passes to run may result in unexpected crashes
and/or assertion failures;
2) The fact that pass managers are not currently designed to support
per-function optimization makes it difficult to find a reasonable way to
implement this new feature.

About point 2. the Idea that came in my mind consisted in making passes
aware of the 'noopt' attribute.
In my experiment:
- I added a virtual method called 'mustAlwaysRun' in class Pass that
'returns true if it is not safe to disable this pass'.
If a pass does not override the default implementation of that method,
then by default it will always return true (i.e. the pass "must
always run" pass even when attribute 'noopt' is specified).
- I then redefined in override that method on all the optimization passes
that could have been safely turned off when attribute noopt was present.
In my experiment, I specifically didn't disable Module Passes;
- Then I modified the 'doInitialize()' 'run*()' and 'doFinalize' methods
in Pass Manger to check for both the presence of attribute noopt AND the
value returned by method 'mustAlwaysRun' called on the current pass
instance.

That experiment seemed to "work" on a few tests and benchmarks.
However:
a) 'noopt' wouldn't really imply no optimization, since not all codegen
optimization passes can be safely disabled. As a result, the assembly
produced for noopt functions had few differences with respect to the
assembly generated for the same functions at -O0;
b) I don't particularly like the idea of making passes "aware" of the
'noopt' attribute. However, I don't know if there is a reasonable way to
implement the noopt attribute without having to re-design how pass
managers work.
  c) Because of a. and b., I am concerned that a change like the one
described above won't be accepted. If so however, I would be really
interested in the feedback from the community. Maybe there are better ways
to implement 'noopt' which I don't know/didn't think about?

As I said, I am not very happy with the proposed solution and any feedback
would be really appreciated at this point.

By the way, here is how I thought the 'noopt' proposal could have been
contributed in terms of patches:

[LLVM IR][Patch 1]

Andrea_DiBiagio@sn.scee.net wrote:

So..
I have investigated more on how a new function attribute to disable
optimization on a per-function basis could be implemented.
At the current state, with the lack of specific support from the pass
managers I found two big problems when trying to implement a prototype
implementation of the new attribute.

Here are the problems found:
1) It is not safe to disable some transform passes in the backend.

Hold on. By 'backend' do you mean LLVM IR FunctionPasses, or LLVM CodeGen MachineFunctionPasses? The former you should be able to turn off, reorder, etc., at will. The latter is a fixed pipeline, you either choose the -O0 pipeline or the -O2 pipeline.

If you find that turning off IR-level passes is triggering assertions, please start filing bugs.

Nick

From: Nick Lewycky <nicholas@mxc.ca>
> So..
> I have investigated more on how a new function attribute to disable
> optimization on a per-function basis could be implemented.
> At the current state, with the lack of specific support from the pass
> managers I found two big problems when trying to implement a prototype
> implementation of the new attribute.
>
> Here are the problems found:
> 1) It is not safe to disable some transform passes in the backend.

Hold on. By 'backend' do you mean LLVM IR FunctionPasses, or LLVM
CodeGen MachineFunctionPasses? The former you should be able to turn
off, reorder, etc., at will. The latter is a fixed pipeline, you either
choose the -O0 pipeline or the -O2 pipeline.

Sorry, I meant CodeGen MachineFunctionPasses.

So..
I have investigated more on how a new function attribute to disable
optimization on a per-function basis could be implemented.
At the current state, with the lack of specific support from the pass
managers I found two big problems when trying to implement a prototype
implementation of the new attribute.

Here are the problems found:
1) It is not safe to disable some transform passes in the backend.
It looks like there are some unwritten dependences between passes and
disrupting the sequence of passes to run may result in unexpected crashes
and/or assertion failures;

This sounds like a bug. It's probably worth bringing up as its own
discussion on llvmdev if it is extremely prevalent, or file PR's (or send
patches fixing it!) if it is just a few isolated cases.

2) The fact that pass managers are not currently designed to support
per-function optimization makes it difficult to find a reasonable way to
implement this new feature.

About point 2. the Idea that came in my mind consisted in making passes
aware of the 'noopt' attribute.
In my experiment:
- I added a virtual method called 'mustAlwaysRun' in class Pass that
'returns true if it is not safe to disable this pass'.
If a pass does not override the default implementation of that method,
then by default it will always return true (i.e. the pass "must
always run" pass even when attribute 'noopt' is specified).
- I then redefined in override that method on all the optimization passes
that could have been safely turned off when attribute noopt was present.
In my experiment, I specifically didn't disable Module Passes;
- Then I modified the 'doInitialize()' 'run*()' and 'doFinalize' methods
in Pass Manger to check for both the presence of attribute noopt AND the
value returned by method 'mustAlwaysRun' called on the current pass
instance.

That experiment seemed to "work" on a few tests and benchmarks.
However:
a) 'noopt' wouldn't really imply no optimization, since not all codegen
optimization passes can be safely disabled. As a result, the assembly
produced for noopt functions had few differences with respect to the
assembly generated for the same functions at -O0;
b) I don't particularly like the idea of making passes "aware" of the
'noopt' attribute. However, I don't know if there is a reasonable way to
implement the noopt attribute without having to re-design how pass
managers work.

A redesign of the pass manager has been on the table for a while and the
need seems more pressing daily. Definitely make sure that this use case is
brought up in any design discussions for the new pass manager.

-- Sean Silva

From: Sean Silva <silvas@purdue.edu>
Here are the problems found:
1) It is not safe to disable some transform passes in the backend.
It looks like there are some unwritten dependences between passes and
disrupting the sequence of passes to run may result in unexpected

crashes

and/or assertion failures;

This sounds like a bug. It's probably worth bringing up as its own
discussion on llvmdev if it is extremely prevalent, or file PR's (or
send patches fixing it!) if it is just a few isolated cases.

It looks like the problem was in my code and it was caused by the lack of
update on the set of preserved analysis; as soon as the problem was fixed,
all the unexpected failures disappeared.

2) The fact that pass managers are not currently designed to support
per-function optimization makes it difficult to find a reasonable way to
implement this new feature.

About point 2. the Idea that came in my mind consisted in making passes
aware of the 'noopt' attribute.
In my experiment:
- I added a virtual method called 'mustAlwaysRun' in class Pass that
'returns true if it is not safe to disable this pass'.
If a pass does not override the default implementation of that method,
then by default it will always return true (i.e. the pass "must
always run" pass even when attribute 'noopt' is specified).
- I then redefined in override that method on all the optimization

passes

that could have been safely turned off when attribute noopt was present.
In my experiment, I specifically didn't disable Module Passes;
- Then I modified the 'doInitialize()' 'run*()' and 'doFinalize'

methods

in Pass Manger to check for both the presence of attribute noopt AND the
value returned by method 'mustAlwaysRun' called on the current pass
instance.

That experiment seemed to "work" on a few tests and benchmarks.
However:
a) 'noopt' wouldn't really imply no optimization, since not all codegen
optimization passes can be safely disabled. As a result, the assembly
produced for noopt functions had few differences with respect to the
assembly generated for the same functions at -O0;
b) I don't particularly like the idea of making passes "aware" of the
'noopt' attribute. However, I don't know if there is a reasonable way to
implement the noopt attribute without having to re-design how pass
managers work.

A redesign of the pass manager has been on the table for a while and
the need seems more pressing daily. Definitely make sure that this
use case is brought up in any design discussions for the new pass

manager.

-- Sean Silva

Sure, I will definitely bring up this as soon as there will be a
discussion about the pass manager.

As I mentioned in my previous post, there could be a way to implement the
'noopt' attribute without having to redesign the pass manager.
However it would require that we explicitly mark function passes that are
expected to not run on a function marked as 'noopt'. That would guide the
pass manager when selecting which passes to initialize/run/finalize on a
'noopt' function.

What I don't like about that approach is the fact that passes would know
about the noopt flag. Not only it would require changes in most of the
already existent passes, but also it would force people who develop
optimization passes to know about the existence of 'noopt' when they
define their pass..

My questions are:
would it be reasonable/acceptable at this point to implement this proposal
before the pass manager is redesigned?
If so, a solution like the one described above would be acceptable? Or are
there better solutions?

Any feedback (either good or bad) would be really appreciated at this
point.

Thanks,
Andrea Di Biagio
SN Systems - Sony Computer Entertainment Group