[RFC] New function attribute, optdebug, for prioritizing debuggability in LLVM passes

This RFC relates to another ongoing discussion: [RFC] Redefine Og/O1 and add a new level of Og

In order to support a new optimization mode that prioritizes the debuggability of generated code instead of only focusing on performance or code size, I’ve implemented a new function attribute optdebug that specifies that the function it applies to should have debuggability prioritized, in the same way that optsize or optminsize specify that code size should be prioritized.

This attribute does not have any specific functions currently, but the first practical change it will make is to disable post-RA instruction scheduling, due to the negative effects on debuggability that it has when paired with the -fextend-lifetimes option enabled, which will also be turned on when prioritizing debuggability.

PR: [LLVM] Add new attribute `optdebug` to optimize for debugging by SLTozer · Pull Request #66632 · llvm/llvm-project · GitHub

1 Like

This seems to be mixing a front-end concern (-fextend-lifetimes) with a backend concern (post-RA scheduling). But it makes sense to have an attribute to propagate -Og-ness to the middle/back-ends, analogous to optnone.

Would optdebug have any conflicts with other attributes? optnone, optsize, etc come to mind.

The main difference between the optimization pipelines is the function simplification pipeline. It would be nice to be able to dynamically choose which function simplification pipeline to run at runtime based on function attributes instead of statically choosing one pipeline for the entire module.

(This would explode the textual representation of the pipeline but oh well)

This seems to be mixing a front-end concern (-fextend-lifetimes) with a backend concern (post-RA scheduling).

That’s true - originally this was achieved with a simple string attribute “disable-post-ra”; as you mentioned , the reason for the change to a more general attribute is so that -Og-ness can be propagated. We could make -fextend-lifetimes apply “disable-post-ra” independently of optdebug if we feel that disabling post-RA instruction scheduling is something we should avoid if we have -Og and not -fextend-lifetimes.

I believe that it’s probably safe to keep just one attribute though, as for the few cases I tested post-RA instruction scheduling was not hugely beneficial to performance while having the potential to damage debug info even without -fextend-lifetimes, but if we want hard numbers before committing to that I can experiment with it.

Would optdebug have any conflicts with other attributes? optnone, optsize, etc come to mind.

optnone currently overrides optsize, and it’d do the same to optdebug. Regarding optsize+optdebug, I don’t currently have any reason to believe there’s a conflict, as they are mostly used to toggle passes on/off; by default we wouldn’t have them both set currently, but if at some point somebody wishes to do so (which may be strongly desired by some embedded developers) the intent is that it should just work, and hopefully result in a program that is smaller and more debuggable than without those flags.

I’m not too familiar with the pass manager text representations, but I imagine there would need to be some conditional function, e.g. "function(ifn<optdebug>(jump-threading,correlated-propagation),loop(loop-deletion,ifn<optsize>(loop-unroll)))". The pass pipelines for default Ox pipelines are already quite busy and hard to read (to my untrained eyes), so I don’t know if it would actually make too much of a difference.

optnone currently overrides optsize, and it’d do the same to optdebug. Regarding optsize+optdebug, I don’t currently have any reason to believe there’s a conflict, as they are mostly used to toggle passes on/off; by default we wouldn’t have them both set currently, but if at some point somebody wishes to do so (which may be strongly desired by some embedded developers) the intent is that it should just work, and hopefully result in a program that is smaller and more debuggable than without those flags.

As I mentioned in the PR, optdebug and optsize/minsize do conflict since optsize/minsize want all optimizations that simplify code, but optdebug does not.

I suppose it depends what you mean by conflict: I was thinking in terms of, “could it make logical sense to have both enabled”, to which I think the answer is yes. There are passes that optsize wants and optdebug doesn’t, but if both act independently on passes that only one cares about, and one takes precedence over the other in cases where their opinions differ, the result could still be meaningful and useful e.g. Os without jump threading, post-RA scheduling, or other passes that are bad for debug info and don’t decrease code size.
It’s also fine however if, for simplicity’s sake, we simply make them mutually exclusive and enforce that via the verifier - I don’t personally have a strong opinion on which option would be better.

That precedence would want to be principled, not by chance, I think, and the precedence is not defined.

This attribute has been added in commit df3478e480b3 (currently awaiting green builds from the buildbots) - no blocking concerns were raised here that weren’t addressed in the review, but I’m open to any further discussion about the attribute or the idea of allowing function attributes to be used as a component of the pass pipeline (which sounds like it might be useful!).

To me optsize is an attribute because it is at least as much about heuristics and thresholds during optimization than “enable/disable individual pass” as whole.
Coincidentally, since you made up an example with loop-unroll, this is exactly what it does I believe: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp#L219-L229