This is an archive of the discontinued LLVM Phabricator instance.

Clean up CFG on JumpThreading
Needs RevisionPublic

Authored by arkangath on Nov 15 2022, 5:11 AM.

Details

Summary

In some loop situations, when the loop blocks become unreachable, JumpThreading would leave dead blocks behind. It could also generate self-referencing instructions (on a dead cfg).

This patch cleans up those blocks.

Diff Detail

Event Timeline

arkangath created this revision.Nov 15 2022, 5:11 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 15 2022, 5:11 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
arkangath requested review of this revision.Nov 15 2022, 5:11 AM
Herald added a project: Restricted Project. · View Herald TranscriptNov 15 2022, 5:11 AM

Note that for unreachable IR, self-referencing, and other forms of invalidity of IR, is legal.

llvm/lib/Transforms/Scalar/JumpThreading.cpp
451

Would it not be sufficient to just add the block into Unreachable set?

Note that for unreachable IR, self-referencing, and other forms of invalidity of IR, is legal.

Yup, I'm aware :)
Nevertheless, it's probably good form to clean up after a pass.

llvm/lib/Transforms/Scalar/JumpThreading.cpp
451

I don't know - the code I copied it from already did this, so I just kept it.

arkangath marked an inline comment as not done.Nov 15 2022, 5:20 AM
nikic added a subscriber: nikic.Dec 19 2022, 5:25 AM

This doesn't really make sense to me. It deletes trivial cycles, but there may be dead cycles that are larger.

This doesn't really make sense to me. It deletes trivial cycles, but there may be dead cycles that are larger.

I don't understand what you mean.
Should the patch be rejected because it doesn't clean *everything* ?

lebedev.ri requested changes to this revision.Dec 19 2022, 5:38 AM

Please can you explain the larger goal you are trying to achieve here?
The minimal test you've added is very minimal, and does not show any ill-behaviour.

llvm/test/Transforms/JumpThreading/self-block.ll
1

The old result is not a misompile: https://godbolt.org/z/Ecf181P75
Unreachable code can take many weird forms.

This revision now requires changes to proceed.Dec 19 2022, 5:38 AM
nikic requested changes to this revision.Dec 19 2022, 5:38 AM

This doesn't really make sense to me. It deletes trivial cycles, but there may be dead cycles that are larger.

I don't understand what you mean.
Should the patch be rejected because it doesn't clean *everything* ?

Yes. Doing this in JumpThreading makes sense only if it avoids issues related to self-referential instructions (inside JumpThreading), and it cannot do so unless it removes all unreachable blocks. For example, I don't think it covers the case from https://reviews.llvm.org/D139783.

If this is *not* about self-referential instructions breaking JumpThreading itself, then you need to make the motivation for this clearer -- for example, are you addressing a phase ordering problem? (If so, please add a phase ordering test.)

I am aware that the IR after the transformation is legal (as I mentioned in a previous comment).

In the larger scope of things, LLVm is being used as a compiler toolchain for a commercial project (sorry for being coy, but you know how big corp is... and I'm not particularly happy about it either).
There is another transform pass being run after JumpThreading which iterates over the blocks on a function, without checking their reachability or checking the CFG. That pass errors beause it finds a self-referencing instruction. Whether that instruction is reachable or not is irrelevant. I suggested to the team to just run CFGSimplify (or CSE I guess), but that has a compile-time cost, and if JumpThreading already knows the blocks are dead, might as well drop them there and then.

As for more complicated inputs, I would welcome an example. I thought that JumpThreading would eventually reduce any unreachable CFG into a self-referencing block. Nevertheless, _some_ clean up is better than no clean up?

llvm/test/Transforms/JumpThreading/self-block.ll
1

Yes, I am aware. What change is being requested?

I am aware that the IR after the transformation is legal (as I mentioned in a previous comment).

In the larger scope of things, LLVm is being used as a compiler toolchain for a commercial project (sorry for being coy, but you know how big corp is... and I'm not particularly happy about it either).
There is another transform pass being run after JumpThreading which iterates over the blocks on a function, without checking their reachability or checking the CFG.

Is that an upstream pass, or a downstream one?

That pass errors beause it finds a self-referencing instruction. Whether that instruction is reachable or not is irrelevant. I suggested to the team to just run CFGSimplify (or CSE I guess), but that has a compile-time cost, and if JumpThreading already knows the blocks are dead, might as well drop them there and then.

As for more complicated inputs, I would welcome an example. I thought that JumpThreading would eventually reduce any unreachable CFG into a self-referencing block. Nevertheless, _some_ clean up is better than no clean up?

nikic added a comment.Dec 19 2022, 6:02 AM

I am aware that the IR after the transformation is legal (as I mentioned in a previous comment).

In the larger scope of things, LLVm is being used as a compiler toolchain for a commercial project (sorry for being coy, but you know how big corp is... and I'm not particularly happy about it either).
There is another transform pass being run after JumpThreading which iterates over the blocks on a function, without checking their reachability or checking the CFG. That pass errors beause it finds a self-referencing instruction. Whether that instruction is reachable or not is irrelevant. I suggested to the team to just run CFGSimplify (or CSE I guess), but that has a compile-time cost, and if JumpThreading already knows the blocks are dead, might as well drop them there and then.

Pass correctness cannot depend on their position in the pipeline. If a pass cannot deal with self-referential IR (by skipping unreachable blocks or in some other way), that's a problem with that pass, not whatever produced the IR.

Is that an upstream pass, or a downstream one?

Ok took me a while to figure it out, but it appears to be a downstream pass which does something like
for (auto &BB : F)

for (auto &I : BB)

and then infinite-recurses when trying to transform a BinaryOperator.

The .ll file I added on the patch is a bugpoint reduced case.
Incidentally, we have a different patch fixing D139783 but that's not relevant for this case.

lebedev.ri resigned from this revision.Jan 12 2023, 4:59 PM

This review may be stuck/dead, consider abandoning if no longer relevant.
Removing myself as reviewer in attempt to clean dashboard.