Disable optimization on basic block level

Hi folks,

During an IR function pass I want to annotate certain basic blocks to avoid later optimization on them. I know I, I can disable optimization on function granularity, but how to do so on basic block level?

Cheers,
Max

I don't think there is any way to do this in LLVM. What would that be useful for?

- Matthias

+CC llvm-dev again!

Hi Matthias,

thanks for your answer.

The reason it would be useful for me is that I am inserting a basic block at IR level as part of a function pass.
However, LLVM's optimization passes are clever enough to optimize my inserted code at IR level.

Of course a compiler is only concerned about the observable effects of a program. Whether there is an extra block or not is not considered part of that. You should probably reconsider what you actually want here. We also cannot really help you with the information given so far, I am pretty sure you have higher goals than just keeping a basic block (otherwise you could just stop the compiler after your pass and the block would stay :slight_smile:

Are there other possibilities to avoid optimization at certain parts of IR code?

Add some instructions triggering observable effects. Typical examples for "tricking" optimizers would be volatile loads or volatile asm blocks (possibly without actual instructions inside).

- Matthias

How do you disable optimization for a function?

I ask because my application often compiles machine-generated code that results in pathological structures that take a long time to optimize, for little benefit. As an example, if a basic block has over a million instructions in it, then DSE can take a while, as it is O(n^2) in the number of instructions in the block. In my application (at least), this block is typically executed only once at run time, and is not part of a loop, so I really don’t care about code quality for that block.

I am able to identify some of these conditions. I would like to tell LLVM to “not try so hard” on the pathological blocks, while keeping optimizations active for ordinary cases.

There is the optnone attribute (see http://llvm.org/docs/LangRef.html#function-attributes ). Passes will skip functions marked with that unless they are essential for code generation.

  • Matthias

How do you disable optimization for a function?

I ask because my application often compiles machine-generated code that
results in pathological structures that take a long time to optimize, for
little benefit. As an example, if a basic block has over a million
instructions in it, then DSE can take a while, as it is O(n^2) in the
number of instructions in the block.

In a lot of cases, we'd rather just fix these optimizers.
There is no reason, for example, for DSE to be N^2.

There are some that are not fixable, but ...

In my application (at least), this block is typically executed only once
at run time, and is not part of a loop, so I really don't care about code
quality for that block.

I am able to identify some of these conditions. I would like to tell LLVM
to "not try so hard" on the pathological blocks, while keeping
optimizations active for ordinary cases.

This is a use case i believe where we'd rather integrate smart limits into
LLVM itself, rather than try to have people control it this way :slight_smile:

I completely agree!

Would be cool to create a suite of extreme inputs, maybe a special llvm test-suite module. This module would contain scripts that produce extreme inputs (long basic blocks, deeply nested loops, utils/create_ladder_graph.py, etc.) In fact I have a python script here as well that generates a few variations of stuff that was interesting to scheduling algos. It would just take someone to setup a proper test-suite and a bot for it and I’d happily contribute more tests :slight_smile:

  • Matthias

Well, I find limited motivation to "optimise" the compiler for artifical
code. Trying to identify real world code that triggers expensive
behavior is a much more useful exercise, IMO.

Joerg

It’s not “artificial” code. It’s real-world code for real applications, generated by scripted code generators.

An example would be a very large Yacc/Bison grammar with many actions inside the user-action switch statement.

Well my practical experience so far has been that the function size for most compiler inputs stays at a reasonable/smallish compared to how much memory most computers have. I assume the humans that write that code can only handle a per-function complexity up to a certain level.

But then there is this really small percentage of users that have written a code generator and they typically don't care anymore about things like block sizes, function length or loop nesting depths. We certainly fix a handful of bugs every year... While these are not the interesting cases to optimize, I think that a good quality compiler should at least handle them in "n log n"/"n^2" time (the cases that end up in bug reports are usually worse than quadratic). Catching the problems before releasing a compiler into the wild would be worthwhile.

- Matthias

Mostly :slight_smile:
But in practice, humans end up triggering the limits most of the time, it
just takes longer.

I've rarely seen an N^3 or worse algorithm in a compiler that didn't end up
a problem over time for real code.

We aren't usually talking about things that are fundamentally impossible in
faster time bounds.
We are talking about things that we've skimped on the implementation for
various reasons (sometimes very good!) and decided not to do faster.

IE to me there is a big difference between, say, andersen's points-to,
which is N^3 worst case, but linear with good implementations in practice,
and say, "rando algorithm i came up with for DSE or something that is N^3
because i don't feel like implementing one of the good ones"
The former, yeah, we do what we can
The latter, those things tend to be the problems over time.

IE something may be better than nothing, but over time, if we don't fix it,
that something ends up very very bad.

If you are doing a thing, and it's a sane thing to do, and your problem is
you just have generated tooo much code for the compiler, that is important
to me.
If you are doing a thing, it's crazy, or your code generator just flat out
sucks, less important.

I definitely agree with both of you.
Historically we've been in a sittuation where we don't discover
compile time problems until too late. As Murphy's law strikes they
generally happen right before cutting a release, and sometimes we have
to commit hardly ideal stopgap solutions (cutoffs, disabling passes
etc..). Having more quality control over this would be definitely an
improvement compared to what we have right now. Joerg, you're the one
who reported really good compile time regressions over time (CVP and
LCSSA come to the top of my mind as I fixed one of them and reviewed
the other), so you should be really aware of the pain derived from
discovering these problems too late :wink: