Skip to content
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

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

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

ayokunle321
Copy link
Contributor

@ayokunle321 ayokunle321 commented Feb 13, 2025

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 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

mlir/lib/Dialect/Affine/IR/AffineOps.cpp Outdated Show resolved Hide resolved
mlir/lib/Dialect/Affine/IR/AffineOps.cpp Outdated Show resolved Hide resolved
@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
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.

3 participants