Skip to content

[mlir][affine] Add Check for 'affine.for' Bound Map Results #127105

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 6 commits into from
Feb 22, 2025

Conversation

ayokunle321
Copy link
Contributor

@ayokunle321 ayokunle321 commented Feb 13, 2025

Fixes issue #120001.

I learnt that we shouldn’t assert on IR that can be constructed and passes the verifier. In this case, it appears that the verifier is insufficiently strict and should be tightened. Since the maps used in the affine.for op do not have results, I believe the IR verification should fail however it does not.

Minimal example:

#map = affine_map<() -> ()>
module {
func.func @main() {
affine.for %arg0 = max #map() to min #map() {
}
return
}
}

This gets past the verifier, but I added a check to see that it causes an error if an affine.for has a bound map with no result.

@krzysz00 @matthias-springer @kazutakahirata @MaheshRavishankar Open to any comments or clarifications :)

@llvmbot
Copy link
Member

llvmbot commented Feb 13, 2025

@llvm/pr-subscribers-mlir

Author: Ayokunle Amodu (ayokunle321)

Changes

Related to issue #120001.

I learnt that we shouldn’t assert on IR that can be constructed and passes the verifier. In this case, it appears that the verifier is insufficiently strict and should be tightened. Since the maps used in the affine.for op below do not have results, the IR verification should fail however it does not.

Minimal example:

#map = affine_map<() -> ()>
module {
func.func @main() {
affine.for %arg0 = max #map() to min #map() {
}
return
}
}

This gets past the verifier, but I added a check to see that it causes an error if an affine.for has a bound map with no result.

Open to any comments or clarifications :)


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

2 Files Affected:

  • (modified) mlir/lib/Dialect/Affine/IR/AffineOps.cpp (+6)
  • (modified) mlir/test/Dialect/Affine/invalid.mlir (+22)
diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
index 147f5dd7a24b6..06e26e887b050 100644
--- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
+++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
@@ -1903,6 +1903,12 @@ LogicalResult AffineForOp::verifyRegions() {
                                              getUpperBoundMap().getNumDims())))
       return failure();
 
+  if (getLowerBoundMap().getNumResults() < 1)
+    return emitOpError("expected lower bound map to have at least one result");
+
+  if (getUpperBoundMap().getNumResults() < 1)
+    return emitOpError("expected upper bound map to have at least one result");
+    
   unsigned opNumResults = getNumResults();
   if (opNumResults == 0)
     return success();
diff --git a/mlir/test/Dialect/Affine/invalid.mlir b/mlir/test/Dialect/Affine/invalid.mlir
index 44e484b9ba598..5a3243cb074c1 100644
--- a/mlir/test/Dialect/Affine/invalid.mlir
+++ b/mlir/test/Dialect/Affine/invalid.mlir
@@ -523,3 +523,25 @@ func.func @dynamic_dimension_index() {
   }) : () -> ()
   return
 }
+
+// -----
+
+#map = affine_map<() -> ()>
+#map1 = affine_map<() -> (1)>
+func.func @no_lower_bound() {
+  // expected-error@+1 {{'affine.for' op expected lower bound map to have at least one result}}
+  affine.for %i = max #map() to min #map1() {
+  }
+  return
+}
+
+// -----
+
+#map = affine_map<() -> ()>
+#map1 = affine_map<() -> (1)>
+func.func @no_upper_bound() {
+  // expected-error@+1 {{'affine.for' op expected upper bound map to have at least one result}}
+  affine.for %i = max #map1() to min #map() {
+  }
+  return
+}

@llvmbot
Copy link
Member

llvmbot commented Feb 13, 2025

@llvm/pr-subscribers-mlir-affine

Author: Ayokunle Amodu (ayokunle321)

Changes

Related to issue #120001.

I learnt that we shouldn’t assert on IR that can be constructed and passes the verifier. In this case, it appears that the verifier is insufficiently strict and should be tightened. Since the maps used in the affine.for op below do not have results, the IR verification should fail however it does not.

Minimal example:

#map = affine_map<() -> ()>
module {
func.func @main() {
affine.for %arg0 = max #map() to min #map() {
}
return
}
}

This gets past the verifier, but I added a check to see that it causes an error if an affine.for has a bound map with no result.

Open to any comments or clarifications :)


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

2 Files Affected:

  • (modified) mlir/lib/Dialect/Affine/IR/AffineOps.cpp (+6)
  • (modified) mlir/test/Dialect/Affine/invalid.mlir (+22)
diff --git a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
index 147f5dd7a24b6..06e26e887b050 100644
--- a/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
+++ b/mlir/lib/Dialect/Affine/IR/AffineOps.cpp
@@ -1903,6 +1903,12 @@ LogicalResult AffineForOp::verifyRegions() {
                                              getUpperBoundMap().getNumDims())))
       return failure();
 
+  if (getLowerBoundMap().getNumResults() < 1)
+    return emitOpError("expected lower bound map to have at least one result");
+
+  if (getUpperBoundMap().getNumResults() < 1)
+    return emitOpError("expected upper bound map to have at least one result");
+    
   unsigned opNumResults = getNumResults();
   if (opNumResults == 0)
     return success();
diff --git a/mlir/test/Dialect/Affine/invalid.mlir b/mlir/test/Dialect/Affine/invalid.mlir
index 44e484b9ba598..5a3243cb074c1 100644
--- a/mlir/test/Dialect/Affine/invalid.mlir
+++ b/mlir/test/Dialect/Affine/invalid.mlir
@@ -523,3 +523,25 @@ func.func @dynamic_dimension_index() {
   }) : () -> ()
   return
 }
+
+// -----
+
+#map = affine_map<() -> ()>
+#map1 = affine_map<() -> (1)>
+func.func @no_lower_bound() {
+  // expected-error@+1 {{'affine.for' op expected lower bound map to have at least one result}}
+  affine.for %i = max #map() to min #map1() {
+  }
+  return
+}
+
+// -----
+
+#map = affine_map<() -> ()>
+#map1 = affine_map<() -> (1)>
+func.func @no_upper_bound() {
+  // expected-error@+1 {{'affine.for' op expected upper bound map to have at least one result}}
+  affine.for %i = max #map1() to min #map() {
+  }
+  return
+}

@ayokunle321 ayokunle321 changed the title [mlir][linalg} Add check for affine.for map bounds [mlir][linalg] Add check for 'affine.for' map bound results Feb 13, 2025
Copy link

⚠️ We detected that you are using a GitHub private e-mail address to contribute to the repo.
Please turn off Keep my email addresses private setting in your account.
See LLVM Discourse for more information.

Copy link

github-actions bot commented Feb 13, 2025

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

Copy link
Contributor

@krzysz00 krzysz00 left a comment

Choose a reason for hiding this comment

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

Changes approved, but you don't need to comment what the code does

@ayokunle321 ayokunle321 changed the title [mlir][linalg] Add check for 'affine.for' map bound results [mlir][affine] Add Check for 'affine.for' Bound Map Results Feb 13, 2025
@bondhugula
Copy link
Contributor

Please rebase, retest, and merge.

@bondhugula
Copy link
Contributor

Looks like this has exposed another issue/bug to fix. I can take that over if you prefer.

@ayokunle321
Copy link
Contributor Author

@bondhugula sure you can take over. So I should not worry about rebasing and retesting?

@bondhugula
Copy link
Contributor

@bondhugula sure you can take over. So I should not worry about rebasing and retesting?

I'll debug the issue exposed, fix it and then we'll merge this.

@bondhugula
Copy link
Contributor

@bondhugula sure you can take over. So I should not worry about rebasing and retesting?

The failing test case is one where an invalid IR was being generated unchecked to start with. It's now exposed with this PR.

// CHECK-LABEL: func @affine_parallel

Created this issue for it: #127808
I'll post the fix and you can merge this post rebase.

@bondhugula
Copy link
Contributor

bondhugula commented Feb 19, 2025

@bondhugula sure you can take over. So I should not worry about rebasing and retesting?

The failing test case is one where an invalid IR was being generated unchecked to start with. It's now exposed with this PR.

// CHECK-LABEL: func @affine_parallel

Created this issue for it: #127808 I'll post the fix and you can merge this post rebase.

Fixed by #127809

@ayokunle321 ayokunle321 force-pushed the affine-loop-bounds-check branch from 001b1ff to b07e112 Compare February 19, 2025 19:12
@ayokunle321
Copy link
Contributor Author

@bondhugula I did was able to successfully rebase on my local without conflicts:
Screenshot 2025-02-19 at 1 39 16 PM

however GitHub's showing this:
Screenshot 2025-02-19 at 1 36 22 PM

Any suggestions?

@@ -2001,8 +2001,15 @@ static LogicalResult generateCopy(
}

SmallVector<AffineMap, 4> lbMaps(rank), ubMaps(rank);
for (unsigned i = 0; i < rank; ++i)
for (unsigned i = 0; i < rank; ++i) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Duplicate with #127809.

Copy link
Contributor

Choose a reason for hiding this comment

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

This change has been merged. The first commit can be dropped during rebase.

@bondhugula
Copy link
Contributor

@bondhugula I did was able to successfully rebase on my local without conflicts:
Any suggestions?

#127809 has been merged into main now. Please rebase and test.

@bondhugula
Copy link
Contributor

@bondhugula I did was able to successfully rebase on my local without conflicts:
Any suggestions?

#127809 has been merged into main now. Please rebase and test.

You can simply do git pull <upstream> main --rebase and then do a force push (to your fork branch). Then squash and merge from here.

@ayokunle321 ayokunle321 force-pushed the affine-loop-bounds-check branch 2 times, most recently from 0790e02 to 64b6566 Compare February 21, 2025 00:50
@ayokunle321 ayokunle321 force-pushed the affine-loop-bounds-check branch from 64b6566 to 6cead0b Compare February 21, 2025 00:53
@ayokunle321
Copy link
Contributor Author

You can simply do git pull <upstream> main --rebase and then do a force push (to your fork branch). Then squash and merge from here.

Screenshot 2025-02-20 at 5 54 10 PM Screenshot 2025-02-20 at 5 55 17 PM

So I just did that exactly on my local, but it's still saying saying merging is blocked - and I don't have any merge commits. Have no clue why.

@bondhugula bondhugula merged commit dc72a93 into llvm:main Feb 22, 2025
11 checks passed
@bondhugula
Copy link
Contributor

You can simply do git pull <upstream> main --rebase and then do a force push (to your fork branch). Then squash and merge from here.

Screenshot 2025-02-20 at 5 54 10 PM Screenshot 2025-02-20 at 5 55 17 PM
So I just did that exactly on my local, but it's still saying saying merging is blocked - and I don't have any merge commits. Have no clue why.

You need to have selected "Rebase branch" among the two options on the side. I did it and it had not issue squash and merge.

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