Skip to content

[LoopInterchange] Ignore the cost-model, force interchange if legal #148858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jul 18, 2025

Conversation

sjoerdmeijer
Copy link
Collaborator

This is and has been proven useful for testing purposes, to get more test coverage.

This is useful for testing purposes, to get more test coverage.
@llvmbot
Copy link
Member

llvmbot commented Jul 15, 2025

@llvm/pr-subscribers-llvm-transforms

Author: Sjoerd Meijer (sjoerdmeijer)

Changes

This is and has been proven useful for testing purposes, to get more test coverage.


Full diff: https://github.com/llvm/llvm-project/pull/148858.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Scalar/LoopInterchange.cpp (+6-1)
  • (modified) llvm/test/Transforms/LoopInterchange/profitability-vectorization.ll (+4)
diff --git a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
index a5008907b9014..b62c25d52861b 100644
--- a/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopInterchange.cpp
@@ -66,6 +66,10 @@ static cl::opt<unsigned int> MaxMemInstrCount(
         "in the dependency matrix. Higher value may lead to more interchanges "
         "at the cost of compile-time"));
 
+static cl::opt<bool> ForceLoopInterchange(
+    "loop-interchange-force", cl::init(false), cl::Hidden,
+    cl::desc("Ignore the cost model, and force interchange if it is legal"));
+
 namespace {
 
 using LoopVector = SmallVector<Loop *, 8>;
@@ -599,7 +603,8 @@ struct LoopInterchange {
     }
     LLVM_DEBUG(dbgs() << "Loops are legal to interchange\n");
     LoopInterchangeProfitability LIP(OuterLoop, InnerLoop, SE, ORE);
-    if (!LIP.isProfitable(InnerLoop, OuterLoop, InnerLoopId, OuterLoopId,
+    if (!ForceLoopInterchange &&
+        !LIP.isProfitable(InnerLoop, OuterLoop, InnerLoopId, OuterLoopId,
                           DependencyMatrix, CCM)) {
       LLVM_DEBUG(dbgs() << "Interchanging loops not profitable.\n");
       return false;
diff --git a/llvm/test/Transforms/LoopInterchange/profitability-vectorization.ll b/llvm/test/Transforms/LoopInterchange/profitability-vectorization.ll
index 16952a66aa78e..59196bbbb9c00 100644
--- a/llvm/test/Transforms/LoopInterchange/profitability-vectorization.ll
+++ b/llvm/test/Transforms/LoopInterchange/profitability-vectorization.ll
@@ -2,6 +2,10 @@
 ; RUN:     -pass-remarks-output=%t -disable-output
 ; RUN: FileCheck -input-file %t --check-prefix=PROFIT-CACHE %s
 
+; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 \
+; RUN:     -pass-remarks-output=%t -disable-output -loop-interchange-force=true
+; RUN: FileCheck -input-file %t --check-prefix=PROFIT-VEC %s
+
 ; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 \
 ; RUN:     -pass-remarks-output=%t -disable-output -loop-interchange-profitabilities=vectorize,cache,instorder
 ; RUN: FileCheck -input-file %t --check-prefix=PROFIT-VEC %s

static cl::opt<bool> ForceLoopInterchange(
"loop-interchange-force", cl::init(false), cl::Hidden,
cl::desc("Ignore the cost model, and force interchange if it is legal"));

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be better if we call it -loop-interchange-ignore-cost-model? -force seems it covers wider scenarios.

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about adding something like force here instead? I personally think it could be useful in some cases, e.g., we can specify -loop-interchange-profitabilities=cache,force. The behavior is slightly different from -loop-interchange-profitabilities=cache. The former will interchange the loops if it is not unprofitable for the cache, while the latter will perform the interchange if it is profitable for the cache. The result will differ in cases where it is unclear whether interchanging is profitable or unprofitable for the cache.

Comment on lines 5 to 7
; RUN: opt < %s -passes=loop-interchange -cache-line-size=64 \
; RUN: -pass-remarks-output=%t -disable-output -loop-interchange-force=true
; RUN: FileCheck -input-file %t --check-prefix=PROFIT-VEC %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add another test case? Interchanging the loops in this test would be beneficial in perspective of vectorizatoin (i.e., isProfitableForVectorization returns true in this case) because it makes the innermost loop vectorizable. It would be ideal to add a case where interchanging is definitely unprofitable.

@sjoerdmeijer
Copy link
Collaborator Author

What do you think about adding something like force here instead? I personally think it could be useful in some cases, e.g., we can specify -loop-interchange-profitabilities=cache,force. The behavior is slightly different from -loop-interchange-profitabilities=cache. The former will interchange the loops if it is not unprofitable for the cache, while the latter will perform the interchange if it is profitable for the cache. The result will differ in cases where it is unclear whether interchanging is profitable or unprofitable for the cache.

Yeah, I don't mind adding it there, that makes sense. I am a little bit undecided, but I am thinking that the combination cache,force is not a very intuitive option to read, it is not very clear what that means. My question is if we can come up with something that is clearer.

But I see what you would like to achieve, so let me summarise the kind of 3 states that we could have:

  • I would like an option to ignore the cost-model entirely, i.e. to force interchange.
  • Your suggestion is to prefer interchange if the cost-model is undecided. That makes sense.
  • and what we already have, to have it entirely driven by the cost-model (what we currently have, nothing changes for this case).

The first one could be achieved by -loop-interchange-profitabilities=force, so I will add that one first, to help my test coverage.

I might do this in a follow up with another patch to address the issue when the cost-model is undecided. I am not totally in love with this, but I think I will add options:

  • -loop-interchange-profitabilities=cache-force
  • -loop-interchange-profitabilities=vectorize-force
  • -loop-interchange-profitabilities=instorder-force

to prefer interchange when the profitability is not clear. I think a separate option cache-force is a bit clearer than a comma separate list and giving a special meaning to the previous option.

@kasuga-fj
Copy link
Contributor

kasuga-fj commented Jul 15, 2025

Sorry, I spaced out and forgot to mention what I’m actually concerned about: the behavior when both --loop-interchange-profitabilities=... and --loop-interchange-force=1 are specified. Which one should take precedence in this case? It depends on the implementation. Of course, I think it’s fine to explicitly describe the priority between these options somewhere, but instead of providing two separate options, I’d prefer to have a single option to avoid such complications.

Being able to specify something like --loop-interchange-profitabilities=cache,force is just a small bonus, and doesn’t seem very important to me.

@sjoerdmeijer
Copy link
Collaborator Author

the behavior when both --loop-interchange-profitabilities=... and --loop-interchange-force=1 are specified. Which one should take precedence in this case?

I think that should be forcing interchange, which should be the current behaviour. But I am adding force as an option to --loop-interchange-profitabilities= because I like the idea of having one interface for controlling the cost model. In addition, it solves this problem/unclarity.

Copy link

github-actions bot commented Jul 17, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

Comment on lines 1324 to 1326
// TODO? We ignore the force option when it appears in a list, i.e. it
// should occur as the only option to be effective, as mentioned in the
// help.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// TODO? We ignore the force option when it appears in a list, i.e. it
// should occur as the only option to be effective, as mentioned in the
// help.
shouldInterchange = true;

Comment on lines 1293 to 1297

// Return true if interchange is forced.
if (Profitabilities.size() == 1 && Profitabilities[0] == RuleTy::Ignore)
return true;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary if we handle RuleTy::Ignore properly in the later switch statement.

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orce interchange can occur odd behavior in some cases. Can you add a test to show it? Here is an example:

int A[4][4][4];
for (int i = 0; i < 4; i++)
  for (int j = 1; j < 4; j++)
    for (int k = 0; k < 4; k++)
      A[3-i][j-1][k] = A[i][j][k];

IIUIC, the following scenario happens due to the bubble-sort based heuristic:

  1. Try to interchange the 2nd loop (j-loop) and 3rd loop (k-loop). It is legal, so they are interchanged.
  2. Try to interchange the 1st loop (i-loop) and 2nd loop (k-loop, because the j-loop and k-loop were interchanged). It is illegal, so they are NOT interchanged.
  3. Try to interchange the 2nd loop (k-loop) and 3rd loop (j-loop). It is legal indeed, so they are interchanged again.

Consequently, the first interchange is reverted, and the final loop order remains the same as the original. I believe this is acceptable, considering that the force interchange option is intended for testing purposes. However, it is worth clarifying this behavior with an example.

Copy link
Contributor

@kasuga-fj kasuga-fj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please consider to simplify the test, as @fhahn mentioned.

@@ -1311,6 +1324,9 @@ bool LoopInterchangeProfitability::isProfitable(
shouldInterchange =
isProfitableForVectorization(InnerLoopId, OuterLoopId, DepMatrix);
break;
case RuleTy::Ignore:
// Nothing to do, this has no effect.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe using llvm_unreachable is better?

@@ -0,0 +1,43 @@
; RUN: opt < %s -passes=loop-interchange -pass-remarks-output=%t -disable-output -loop-interchange-profitabilities=ignore -S
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

orce interchange can occur odd behavior in some cases. Can you add a test to show it? Here is an example:

int A[4][4][4];
for (int i = 0; i < 4; i++)
  for (int j = 1; j < 4; j++)
    for (int k = 0; k < 4; k++)
      A[3-i][j-1][k] = A[i][j][k];

IIUIC, the following scenario happens due to the bubble-sort based heuristic:

1. Try to interchange the 2nd loop (j-loop) and 3rd loop (k-loop). It is legal, so they are interchanged.

2. Try to interchange the 1st loop (i-loop) and 2nd loop (k-loop, because the j-loop and k-loop were interchanged). It is illegal, so they are NOT interchanged.

3. Try to interchange the 2nd loop (k-loop) and 3rd loop (j-loop). It is legal indeed, so they are interchanged again.

Consequently, the first interchange is reverted, and the final loop order remains the same as the original. I believe this is acceptable, considering that the force interchange option is intended for testing purposes. However, it is worth clarifying this behavior with an example.

Could you please add this? It's fine to do it in another PR.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, thanks, I will commit this and look at that suggestion early next week.
This allows me to run more testing over the weekend.
I have already found a couple of interchange problems, but can raise issues when this option is available.

@sjoerdmeijer sjoerdmeijer merged commit a9f8143 into llvm:main Jul 18, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants