This is an archive of the discontinued LLVM Phabricator instance.

Sinking or hoisting instructions between loops before fusion
ClosedPublic

Authored by aaronkintel on Jan 24 2022, 1:46 PM.

Details

Summary

Instructions between two adjacent loops will be hoisted above the first loop, or sunk below the second to facilitate loop fusion. Hoisting will be attempted for an instruction that dominates the first loop. Otherwise, sinking this instructions will be attempted.

Instructions with side effects will not be considered for sinking or hoisting. Hoisting/sinking of any instructions between loops will only be performed if all the instructions can be moved. As well, sinking/hoisting is considered for each instruction in isolation, without taking into account sinking/hoisting decisions for other instructions in the preheader.

Diff Detail

Event Timeline

aaronkintel created this revision.Jan 24 2022, 1:46 PM
aaronkintel requested review of this revision.Jan 24 2022, 1:46 PM

Fixed clang-format

Can someone please provide feedback for this review?

Herald added a project: Restricted Project. · View Herald TranscriptMar 2 2022, 7:29 AM
jcranmer-intel requested changes to this revision.Mar 2 2022, 12:45 PM
jcranmer-intel added inline comments.
llvm/lib/Transforms/Scalar/LoopFuse.cpp
71–75

Nit: these include lines should be sorted as appropriate in the main include block above.

974–976

This will unconditionally do the code motion even if (future) profitability checks decide that it's not beneficial to fuse the loops. I think it would be better to hold off on doing so until after we've checked fusion profitability, especially because not doing so means we would get the changed flag wrong in some cases.

1057–1058

I'm convinced this check isn't correct, although I am still trying to figure out which check is the correct one.

1074–1077

Nevertheless, the specific checks you used above generally does not comport with these checks. The first set of checks bans all memory instructions from being checked, and if you drill into canSinkOrHoistInst, almost all of the checks in there are about verifying correctness of memory instructions, with the residual checks of call instructions in large part anyways not being what you wanted anyways, given your choice to override its decision point for DbgInfoIntrinsic.

Offhand, isSafeToSpeculativelyExecute feels like it's the best *starting* point for correctness checks for hoisting. This doesn't cover the dependency checking or domination checks, but you can look at isSafeToMoveBefore in CodeMoverUtils.cpp to understand the general basis for the decision point. If we had a isSafeToMoveAfter, I'd outright just say that using a combination of isSafeToMoveBefore and isSafeToMoveAfter is the best way to go, but we don't, so it would be worth digging into isSafeToMoveBefore anyways to understand its machinery.

This revision now requires changes to proceed.Mar 2 2022, 12:45 PM
aaronkintel edited the summary of this revision. (Show Details)

Responding to comments.

aaronkintel marked 2 inline comments as done.May 10 2022, 8:48 AM

The build failures are all different, and are evidently unrelated to this change.

llvm/lib/Transforms/Scalar/LoopFuse.cpp
1057–1058

Let me know what you thi

1074–1077

I have taken a different approach here by using domination for hoisting and checking the users for sinking. Is this satisfactory?

Can someone please provide feedback for this review? Thanks!

There's two more tests I would like to see here:

  • One test that checks that an intrinsically unmovable instruction (e.g., call to an unknown function) in the preheader blocks hoisting/sinking.
  • Another test (in the same file as above is possible) checks that a memory dependency blocks hoisting/sinking.
llvm/lib/Transforms/Scalar/LoopFuse.cpp
954–955

I don't think this variable is necessary, since if it's false, you've already stopped looking at this loop and have continued to the next one.

1048–1049

Nit: As a general style guidelines, C++11 universal initializer syntax is generally disfavored, as is auto.

The rough guideline I use for auto is that you generally want types to appear exactly once, so when you're assigning the result of a cast (especially the dyn_cast checks), then you go ahead and use auto, but otherwise, you generally stay away from it.

1071

Nit: This probably meant should be named CanHoistInst.

1074–1075

You should be able to replace this with bool OpHoisted = is_contained(SafeTohoist, OpInst); (may need to include STLExtras.h).

1078

Nit: OpHoisted should precede the dominator check.

1093–1095

The first half of the or statement here subsumes the second half, does it not?

1339–1341

You mean += here, not =.

llvm/test/Transforms/LoopFusion/no_sink_hoist.ll
3–5

For negative fusion checks, I think a test along the lines of llvm/test/Transforms/LoopFusion/cannot_fuse.ll is more appropriate.

llvm/test/Transforms/LoopFusion/no_sink_hoist_inner_barier.ll
1 ↗(On Diff #426995)

Nit: Too many semicolons.

Also, the file's name should be "no_sink_hoist_inner_barrier.ll"

llvm/test/Transforms/LoopFusion/sink_preheader.ll
2

For this test, I'd strongly prefer to see update_test_checks.py-style checks.

aaronkintel marked 2 inline comments as done.

Responding to reviewer comments.

aaronkintel marked 8 inline comments as done.Jun 13 2022, 6:03 AM

Can someone please provide feedback for this review? Thanks!

llvm/test/Transforms/LoopFusion/sink_preheader.ll
2

Is this what you mean by "update_test_checks.py-style"?

Build failures unrelated to change.

I'd still like to see two more tests:

  • One test that checks that an intrinsically unmovable instruction (e.g., call to an unknown function) in the preheader blocks hoisting/sinking.
  • Another test (in the same file as above is possible) checks that a memory dependency blocks hoisting/sinking.

Other than that, and the nit I'm seeing, looking good.

llvm/lib/Transforms/Scalar/LoopFuse.cpp
1074

I'd still like to see this and the later if statement not using C++11 universal initialization.

Responding to comments. Fixed initialization of variable and added lit tests for ensuring that mem insts and undefined functions are not moved from the preheader of the second loop.

jcranmer-intel added inline comments.Jul 6 2022, 1:39 PM
llvm/test/Transforms/LoopFusion/no_sink_hoist_store.ll
26

The alloca should be in the entry block for this test, should it not?

aaronkintel marked an inline comment as done.

Moving alloca to preheader in no_sink_hoist_store.ll

aaronkintel marked an inline comment as done.Jul 20 2022, 8:38 AM

Can someone please provide feedback for this review?

llvm/test/Transforms/LoopFusion/no_sink_hoist_store.ll
26

Do you mean it should be:
pre1:

%ptr = alloca i32

....
pre2:

store i32 3, i32* %ptr

What is the reason for this?

jcranmer-intel accepted this revision.Jul 25 2022, 2:19 PM
jcranmer-intel added inline comments.
llvm/test/Transforms/LoopFusion/no_sink_hoist_store.ll
26

alloca instructions are generally expected to occur at the beginning of the function's entry block; if they're not, then they're a dynamic alloca block, which can have unexpected impacts on other optimizations. Unless there's a good reason for the alloca not to live in the entry block, then they should be generated in the entry block.

This revision is now accepted and ready to land.Jul 25 2022, 2:19 PM
This revision was landed with ongoing or failed builds.Jul 27 2022, 3:57 AM
This revision was automatically updated to reflect the committed changes.