[Mlir-commits] [mlir] [MLIR][Affine] Reject affine-loop-unroll-jam on dependence-violating nests (PR #196554)

llvmlistbot at llvm.org llvmlistbot at llvm.org
Fri May 8 08:42:12 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-mlir

Author: Soowon Jeong (swjng)

<details>
<summary>Changes</summary>

## Summary

`mlir::affine::loopUnrollJamByFactor` performs no memory-dependence
legality check. Loops whose distance vector becomes negative-leading
under unroll-and-jam's new iteration order are silently miscompiled
(no diagnostic, no warning). This patch adds a factor-aware gate that
refuses the transformation only when the unroll-jam factor `F` is large
enough to actually reorder the dep.

## Reproducer

```mlir
func.func @<!-- -->stencil(%arr: memref<5x5xi32>) {
  affine.for %i = 1 to 5 {
    affine.for %j = 0 to 4 {
      %v = affine.load %arr[%i - 1, %j + 1] : memref<5x5xi32>
      affine.store %v, %arr[%i, %j] : memref<5x5xi32>
    }
  }
  return
}
```

Initialize \`arr[i, j] = 10*i + j\` and run end-to-end via
\`mlir-opt -lower-affine ... | mlir-runner\`:

| Pipeline | Row 2 of output |
| --- | --- |
| no \`-affine-loop-unroll-jam\` | \`[2, 3, 4, 14, 24]\` (correct) |
| \`-affine-loop-unroll-jam=unroll-jam-factor=2\` | \`[11, 12, 13, 14, 24]\` |
| \`unroll-jam-factor=4\` | \`[11, 12, 13, 14, 24]\` (no propagation) |

Reproduces on LLVM 17, LLVM 22, and current \`origin/main\`.

## Why

Unroll-and-jam at factor \`F\` strip-mines the outer loop into
\`F\`-iteration stripes and interchanges the intra-stripe iv with the
inner ivs of the perfectly nested band. For a dependence with outer
distance \`d_outer\` and any inner component \`d_inner_k\`:

- \`d_outer >= F\`: the stripe never spans the dep; the interchange does
  not reorder it. Safe.
- \`d_outer == 0\`: the dep is intra-iteration and is preserved by the
  jammed body order. Safe.
- \`0 < d_outer < F\` and any \`d_inner_k < 0\`: backward distance after
  interchange. **Unsafe**.

## Fix

Inline a dependence walk over the perfectly nested band rooted at
\`forOp\` and apply the factor-aware test above. The check is more
precise than reusing \`isTilingValid\`, which ignores \`F\` and would, for
example, refuse \`(2, -1)\` even at \`F = 2\` where the stripe never spans
the dep.

## Test

Two cases appended to \`mlir/test/Dialect/Affine/unroll-jam.mlir\`
(reusing the existing \`factor=2\` RUN line):

- \`@<!-- -->unroll_jam_illegal_flow_dep_1_neg1\` — \`(1, -1)\` stencil; expects
  the loop to remain unchanged.
- \`@<!-- -->unroll_jam_legal_outer_distance_geq_factor\` — \`(2, -1)\` stencil at
  \`F = 2\`; expects \`step 2\` (demonstrates the factor-aware precision
  win over an \`isTilingValid\`-style check).

\`mlir/test/Dialect/Affine/\` (69 tests) regresses cleanly.

---

Patch is 46.57 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/196554.diff


8 Files Affected:

- (modified) mlir/include/mlir-c/IR.h (+6) 
- (modified) mlir/include/mlir/Bindings/Python/IRCore.h (+129) 
- (modified) mlir/lib/Bindings/Python/IRCore.cpp (+283-117) 
- (modified) mlir/lib/CAPI/IR/IR.cpp (+8) 
- (modified) mlir/lib/Dialect/Affine/Utils/LoopUtils.cpp (+41) 
- (modified) mlir/test/CAPI/ir.c (+19) 
- (modified) mlir/test/Dialect/Affine/unroll-jam.mlir (+40) 
- (modified) mlir/test/python/ir/location.py (+104-55) 


``````````diff
diff --git a/mlir/include/mlir-c/IR.h b/mlir/include/mlir-c/IR.h
index 8d30051d615f4..fdff79f8b67dd 100644
--- a/mlir/include/mlir-c/IR.h
+++ b/mlir/include/mlir-c/IR.h
@@ -363,6 +363,12 @@ MLIR_CAPI_EXPORTED bool mlirLocationIsAName(MlirLocation location);
 /// Creates a location with unknown position owned by the given context.
 MLIR_CAPI_EXPORTED MlirLocation mlirLocationUnknownGet(MlirContext context);
 
+/// TypeID Getter for Unknown.
+MLIR_CAPI_EXPORTED MlirTypeID mlirLocationUnknownGetTypeID(void);
+
+/// Checks whether the given location is an Unknown.
+MLIR_CAPI_EXPORTED bool mlirLocationIsAUnknown(MlirLocation location);
+
 /// Gets the context that a location was created with.
 MLIR_CAPI_EXPORTED MlirContext mlirLocationGetContext(MlirLocation location);
 
diff --git a/mlir/include/mlir/Bindings/Python/IRCore.h b/mlir/include/mlir/Bindings/Python/IRCore.h
index b2edcace7298e..0b758d4061d67 100644
--- a/mlir/include/mlir/Bindings/Python/IRCore.h
+++ b/mlir/include/mlir/Bindings/Python/IRCore.h
@@ -337,6 +337,10 @@ class MLIR_PYTHON_API_EXPORTED PyLocation : public BaseContextObject {
   /// is taken by calling this function.
   static PyLocation createFromCapsule(nanobind::object capsule);
 
+  /// Returns the most-derived Location subclass registered for this TypeID,
+  /// or self.
+  nanobind::typed<nanobind::object, PyLocation> maybeDownCast();
+
 private:
   MlirLocation loc;
 };
@@ -1164,6 +1168,131 @@ class MLIR_PYTHON_API_EXPORTED PyStringAttribute
   static void bindDerived(ClassTy &c);
 };
 
+/// CRTP base class for Python classes that subclass Location and should be
+/// castable from it (i.e. via something like FileLineColLoc(loc)).
+template <typename DerivedTy, typename BaseTy = PyLocation>
+class MLIR_PYTHON_API_EXPORTED PyConcreteLocation : public BaseTy {
+public:
+  // Derived classes must define statics for:
+  //   IsAFunctionTy isaFunction
+  //   const char *pyClassName
+  using ClassTy = nanobind::class_<DerivedTy, BaseTy>;
+  using IsAFunctionTy = bool (*)(MlirLocation);
+  using GetTypeIDFunctionTy = MlirTypeID (*)();
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction = nullptr;
+  using Base = PyConcreteLocation;
+
+  PyConcreteLocation() = default;
+  PyConcreteLocation(PyMlirContextRef contextRef, MlirLocation loc)
+      : BaseTy(std::move(contextRef), loc) {}
+  PyConcreteLocation(PyLocation &orig)
+      : PyConcreteLocation(orig.getContext(), castFrom(orig)) {}
+
+  static MlirLocation castFrom(PyLocation &orig) {
+    if (!DerivedTy::isaFunction(orig.get())) {
+      auto origRepr =
+          nanobind::cast<std::string>(nanobind::repr(nanobind::cast(orig)));
+      throw nanobind::value_error((std::string("Cannot cast location to ") +
+                                   DerivedTy::pyClassName + " (from " +
+                                   origRepr + ")")
+                                      .c_str());
+    }
+    return orig.get();
+  }
+
+  static void bind(nanobind::module_ &m) {
+    ClassTy cls(m, DerivedTy::pyClassName, nanobind::is_generic());
+    cls.def(nanobind::init<PyLocation &>(), nanobind::keep_alive<0, 1>(),
+            nanobind::arg("cast_from_loc"));
+    cls.def_prop_ro_static("static_typeid", [](nanobind::object & /*class*/) {
+      if (DerivedTy::getTypeIdFunction)
+        return PyTypeID(DerivedTy::getTypeIdFunction());
+      throw nanobind::attribute_error(
+          (DerivedTy::pyClassName + std::string(" has no typeid.")).c_str());
+    });
+    cls.def("__repr__", [](DerivedTy &self) {
+      PyPrintAccumulator printAccum;
+      printAccum.parts.append(DerivedTy::pyClassName);
+      printAccum.parts.append("(");
+      mlirLocationPrint(self, printAccum.getCallback(),
+                        printAccum.getUserData());
+      printAccum.parts.append(")");
+      return printAccum.join();
+    });
+    if (DerivedTy::getTypeIdFunction) {
+      PyGlobals::get().registerTypeCaster(
+          DerivedTy::getTypeIdFunction(),
+          nanobind::cast<nanobind::callable>(nanobind::cpp_function(
+              [](PyLocation pyLoc) -> DerivedTy { return pyLoc; })),
+          /*replace*/ true);
+    }
+    DerivedTy::bindDerived(cls);
+  }
+
+  /// Implemented by derived classes to add methods to the Python subclass.
+  static void bindDerived(ClassTy &m) {}
+};
+
+class MLIR_PYTHON_API_EXPORTED PyUnknownLocation
+    : public PyConcreteLocation<PyUnknownLocation> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirLocationIsAUnknown;
+  static constexpr const char *pyClassName = "UnknownLoc";
+  using PyConcreteLocation::PyConcreteLocation;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLocationUnknownGetTypeID;
+
+  static void bindDerived(ClassTy &c);
+};
+
+class MLIR_PYTHON_API_EXPORTED PyFileLineColLocation
+    : public PyConcreteLocation<PyFileLineColLocation> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirLocationIsAFileLineColRange;
+  static constexpr const char *pyClassName = "FileLineColLoc";
+  using PyConcreteLocation::PyConcreteLocation;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLocationFileLineColRangeGetTypeID;
+
+  static void bindDerived(ClassTy &c);
+};
+
+class MLIR_PYTHON_API_EXPORTED PyNameLocation
+    : public PyConcreteLocation<PyNameLocation> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirLocationIsAName;
+  static constexpr const char *pyClassName = "NameLoc";
+  using PyConcreteLocation::PyConcreteLocation;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLocationNameGetTypeID;
+
+  static void bindDerived(ClassTy &c);
+};
+
+class MLIR_PYTHON_API_EXPORTED PyCallSiteLocation
+    : public PyConcreteLocation<PyCallSiteLocation> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirLocationIsACallSite;
+  static constexpr const char *pyClassName = "CallSiteLoc";
+  using PyConcreteLocation::PyConcreteLocation;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLocationCallSiteGetTypeID;
+
+  static void bindDerived(ClassTy &c);
+};
+
+class MLIR_PYTHON_API_EXPORTED PyFusedLocation
+    : public PyConcreteLocation<PyFusedLocation> {
+public:
+  static constexpr IsAFunctionTy isaFunction = mlirLocationIsAFused;
+  static constexpr const char *pyClassName = "FusedLoc";
+  using PyConcreteLocation::PyConcreteLocation;
+  static constexpr GetTypeIDFunctionTy getTypeIdFunction =
+      mlirLocationFusedGetTypeID;
+
+  static void bindDerived(ClassTy &c);
+};
+
 /// Wrapper around the generic MlirValue.
 /// Values are managed completely by the operation that resulted in their
 /// definition. For op result value, this is the operation that defines the
diff --git a/mlir/lib/Bindings/Python/IRCore.cpp b/mlir/lib/Bindings/Python/IRCore.cpp
index 0fa84a11f2f35..f92d4c14ceb16 100644
--- a/mlir/lib/Bindings/Python/IRCore.cpp
+++ b/mlir/lib/Bindings/Python/IRCore.cpp
@@ -1888,6 +1888,23 @@ nb::typed<nb::object, PyAttribute> PyAttribute::maybeDownCast() {
   return typeCaster.value()(thisObj);
 }
 
+//------------------------------------------------------------------------------
+// PyLocation::maybeDownCast.
+//------------------------------------------------------------------------------
+
+nb::typed<nb::object, PyLocation> PyLocation::maybeDownCast() {
+  MlirAttribute locAttr = mlirLocationGetAttribute(this->get());
+  MlirTypeID mlirTypeID = mlirAttributeGetTypeID(locAttr);
+  assert(!mlirTypeIDIsNull(mlirTypeID) &&
+         "mlirTypeID was expected to be non-null.");
+  std::optional<nb::callable> typeCaster = PyGlobals::get().lookupTypeCaster(
+      mlirTypeID, mlirAttributeGetDialect(locAttr));
+  nb::object thisObj = nb::cast(this, nb::rv_policy::move);
+  if (!typeCaster)
+    return thisObj;
+  return typeCaster.value()(thisObj);
+}
+
 //------------------------------------------------------------------------------
 // PyNamedAttribute.
 //------------------------------------------------------------------------------
@@ -3051,6 +3068,191 @@ void populateRoot(nb::module_ &m) {
       "Register a value caster for casting MLIR values to custom user values.");
 }
 
+//------------------------------------------------------------------------------
+// Location subclass bindDerived implementations.
+//------------------------------------------------------------------------------
+
+void PyUnknownLocation::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](DefaultingPyMlirContext context) {
+        return PyUnknownLocation(context->getRef(),
+                                 mlirLocationUnknownGet(context->get()));
+      },
+      "context"_a = nb::none(),
+      "Gets a Location representing an unknown location.");
+}
+
+void PyFileLineColLocation::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](std::string filename, int line, int col,
+         DefaultingPyMlirContext context) {
+        return PyFileLineColLocation(
+            context->getRef(),
+            mlirLocationFileLineColGet(context->get(),
+                                       toMlirStringRef(filename), line, col));
+      },
+      "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
+      "Gets a FileLineColLoc for a file, line, and column.");
+  c.def_static(
+      "get",
+      [](std::string filename, int startLine, int startCol, int endLine,
+         int endCol, DefaultingPyMlirContext context) {
+        return PyFileLineColLocation(
+            context->getRef(), mlirLocationFileLineColRangeGet(
+                                   context->get(), toMlirStringRef(filename),
+                                   startLine, startCol, endLine, endCol));
+      },
+      "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a, "end_col"_a,
+      "context"_a = nb::none(),
+      "Gets a FileLineColLoc spanning a file and line/column range.");
+  c.def_prop_ro(
+      "filename",
+      [](PyFileLineColLocation &self) {
+        return mlirIdentifierStr(
+            mlirLocationFileLineColRangeGetFilename(self.get()));
+      },
+      "Gets the filename from a `FileLineColLoc`.");
+  c.def_prop_ro(
+      "start_line",
+      [](PyFileLineColLocation &self) {
+        return mlirLocationFileLineColRangeGetStartLine(self.get());
+      },
+      "Gets the start line number from a `FileLineColLoc`.");
+  c.def_prop_ro(
+      "start_col",
+      [](PyFileLineColLocation &self) {
+        return mlirLocationFileLineColRangeGetStartColumn(self.get());
+      },
+      "Gets the start column number from a `FileLineColLoc`.");
+  c.def_prop_ro(
+      "end_line",
+      [](PyFileLineColLocation &self) {
+        return mlirLocationFileLineColRangeGetEndLine(self.get());
+      },
+      "Gets the end line number from a `FileLineColLoc`.");
+  c.def_prop_ro(
+      "end_col",
+      [](PyFileLineColLocation &self) {
+        return mlirLocationFileLineColRangeGetEndColumn(self.get());
+      },
+      "Gets the end column number from a `FileLineColLoc`.");
+}
+
+void PyNameLocation::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](std::string name, std::optional<PyLocation> childLoc,
+         DefaultingPyMlirContext context) {
+        return PyNameLocation(
+            context->getRef(),
+            mlirLocationNameGet(context->get(), toMlirStringRef(name),
+                                childLoc
+                                    ? childLoc->get()
+                                    : mlirLocationUnknownGet(context->get())));
+      },
+      "name"_a, "child_loc"_a = nb::none(), "context"_a = nb::none(),
+      "Gets a NameLoc with an optional child location.");
+  c.def_prop_ro(
+      "name_str",
+      [](PyNameLocation &self) {
+        return mlirIdentifierStr(mlirLocationNameGetName(self.get()));
+      },
+      "Gets the name string from a `NameLoc`.");
+  c.def_prop_ro(
+      "child_loc",
+      [](PyNameLocation &self) {
+        return PyLocation(self.getContext(),
+                          mlirLocationNameGetChildLoc(self.get()))
+            .maybeDownCast();
+      },
+      "Gets the child location from a `NameLoc`.");
+}
+
+void PyCallSiteLocation::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](PyLocation callee, const std::vector<PyLocation> &frames,
+         DefaultingPyMlirContext context) {
+        if (frames.empty())
+          throw nb::value_error("No caller frames provided.");
+        MlirLocation caller = frames.back().get();
+        for (size_t index = frames.size() - 1; index-- > 0;) {
+          caller = mlirLocationCallSiteGet(frames[index].get(), caller);
+        }
+        return PyCallSiteLocation(
+            context->getRef(), mlirLocationCallSiteGet(callee.get(), caller));
+      },
+      "callee"_a, "frames"_a, "context"_a = nb::none(),
+      "Gets a CallSiteLoc chaining a callee and one or more caller frames.");
+  c.def_prop_ro(
+      "callee",
+      [](PyCallSiteLocation &self) {
+        return PyLocation(self.getContext(),
+                          mlirLocationCallSiteGetCallee(self.get()))
+            .maybeDownCast();
+      },
+      "Gets the callee location from a `CallSiteLoc`.");
+  c.def_prop_ro(
+      "caller",
+      [](PyCallSiteLocation &self) {
+        return PyLocation(self.getContext(),
+                          mlirLocationCallSiteGetCaller(self.get()))
+            .maybeDownCast();
+      },
+      "Gets the caller location from a `CallSiteLoc`.");
+}
+
+void PyFusedLocation::bindDerived(ClassTy &c) {
+  c.def_static(
+      "get",
+      [](const std::vector<PyLocation> &pyLocations,
+         std::optional<PyAttribute> metadata, DefaultingPyMlirContext context) {
+        std::vector<MlirLocation> locations;
+        locations.reserve(pyLocations.size());
+        for (const PyLocation &pyLocation : pyLocations)
+          locations.push_back(pyLocation.get());
+        MlirLocation location = mlirLocationFusedGet(
+            context->get(), locations.size(), locations.data(),
+            metadata ? metadata->get() : MlirAttribute{0});
+        // Strict: `Location.fused(...)` handles the collapse case.
+        if (!mlirLocationIsAFused(location))
+          throw nb::value_error(
+              "FusedLoc.get would collapse to a non-fused location; use "
+              "Location.fused(...) for the permissive variant.");
+        return PyFusedLocation(context->getRef(), location);
+      },
+      "locations"_a, "metadata"_a = nb::none(), "context"_a = nb::none(),
+      "Gets a FusedLoc from an array of locations and optional metadata. "
+      "Raises if the fuse would collapse to a non-fused location; use "
+      "`Location.fused(...)` for the permissive variant.");
+  c.def_prop_ro(
+      "locations",
+      [](PyFusedLocation &self) {
+        unsigned numLocations = mlirLocationFusedGetNumLocations(self.get());
+        std::vector<MlirLocation> locations(numLocations);
+        if (numLocations)
+          mlirLocationFusedGetLocations(self.get(), locations.data());
+        std::vector<nb::object> pyLocations;
+        pyLocations.reserve(numLocations);
+        for (unsigned i = 0; i < numLocations; ++i)
+          pyLocations.push_back(
+              PyLocation(self.getContext(), locations[i]).maybeDownCast());
+        return pyLocations;
+      },
+      "Gets the list of locations from a `FusedLoc`.");
+  c.def_prop_ro(
+      "metadata",
+      [](PyFusedLocation &self) -> std::optional<PyAttribute> {
+        MlirAttribute metadata = mlirLocationFusedGetMetadata(self.get());
+        if (mlirAttributeIsNull(metadata))
+          return std::nullopt;
+        return PyAttribute(self.getContext(), metadata);
+      },
+      "Gets the metadata attribute from a `FusedLoc`, or None if absent.");
+}
+
 //------------------------------------------------------------------------------
 // Populates the core exports of the 'ir' submodule.
 //------------------------------------------------------------------------------
@@ -3423,122 +3625,52 @@ void populateIRCore(nb::module_ &m) {
           // clang-format on
           "Gets the Location bound to the current thread or raises ValueError.")
       .def_static(
-          "unknown",
-          [](DefaultingPyMlirContext context) {
+          "from_attr",
+          [](PyAttribute &attribute, DefaultingPyMlirContext context) {
             return PyLocation(context->getRef(),
-                              mlirLocationUnknownGet(context->get()));
+                              mlirLocationFromAttribute(attribute))
+                .maybeDownCast();
           },
-          "context"_a = nb::none(),
-          "Gets a Location representing an unknown location.")
+          "attribute"_a, "context"_a = nb::none(),
+          "Gets a Location from a `LocationAttr`.")
+      // Factory shims kept for backward compatibility; return the concrete
+      // subclass. New code should use the subclass `.get()` directly.
       .def_static(
-          "callsite",
-          [](PyLocation callee, const std::vector<PyLocation> &frames,
-             DefaultingPyMlirContext context) {
-            if (frames.empty())
-              throw nb::value_error("No caller frames provided.");
-            MlirLocation caller = frames.back().get();
-            for (size_t index = frames.size() - 1; index-- > 0;) {
-              caller = mlirLocationCallSiteGet(frames[index].get(), caller);
-            }
-            return PyLocation(context->getRef(),
-                              mlirLocationCallSiteGet(callee.get(), caller));
-          },
-          "callee"_a, "frames"_a, "context"_a = nb::none(),
-          "Gets a Location representing a caller and callsite.")
-      .def("is_a_callsite", mlirLocationIsACallSite,
-           "Returns True if this location is a CallSiteLoc.")
-      .def_prop_ro(
-          "callee",
-          [](PyLocation &self) {
-            return PyLocation(self.getContext(),
-                              mlirLocationCallSiteGetCallee(self));
-          },
-          "Gets the callee location from a CallSiteLoc.")
-      .def_prop_ro(
-          "caller",
-          [](PyLocation &self) {
-            return PyLocation(self.getContext(),
-                              mlirLocationCallSiteGetCaller(self));
+          "unknown",
+          [](DefaultingPyMlirContext context) {
+            return PyUnknownLocation(context->getRef(),
+                                     mlirLocationUnknownGet(context->get()));
           },
-          "Gets the caller location from a CallSiteLoc.")
+          "context"_a = nb::none(), "Alias for `UnknownLoc.get()`.")
       .def_static(
           "file",
           [](std::string filename, int line, int col,
              DefaultingPyMlirContext context) {
-            return PyLocation(
+            return PyFileLineColLocation(
                 context->getRef(),
                 mlirLocationFileLineColGet(
                     context->get(), toMlirStringRef(filename), line, col));
           },
           "filename"_a, "line"_a, "col"_a, "context"_a = nb::none(),
-          "Gets a Location representing a file, line and column.")
+          "Alias for `FileLineColLoc.get()`.")
       .def_static(
           "file",
           [](std::string filename, int startLine, int startCol, int endLine,
              int endCol, DefaultingPyMlirContext context) {
-            return PyLocation(context->getRef(),
-                              mlirLocationFileLineColRangeGet(
-                                  context->get(), toMlirStringRef(filename),
-                                  startLine, startCol, endLine, endCol));
+            return PyFileLineColLocation(
+                context->getRef(),
+                mlirLocationFileLineColRangeGet(
+                    context->get(), toMlirStringRef(filename), startLine,
+                    startCol, endLine, endCol));
           },
           "filename"_a, "start_line"_a, "start_col"_a, "end_line"_a,
           "end_col"_a, "context"_a = nb::none(),
-          "Gets a Location representing a file, line and column range.")
-      .def("is_a_file", mlirLocationIsAFileLineColRange,
-           "Returns True if this location is a FileLineColLoc.")
-      .def_prop_ro(
-          "filename",
-          [](PyLocation loc) {
-            return mlirIdentifierStr(
-                mlirLocationFileLineColRangeGetFilename(loc));
-          },
-          "Gets the...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/196554


More information about the Mlir-commits mailing list