Add OpaqueLoc to MLIR locations.
authorMLIR Team <no-reply@google.com>
Mon, 7 Oct 2019 12:05:07 +0000 (05:05 -0700)
committerA. Unique TensorFlower <gardener@tensorflow.org>
Mon, 7 Oct 2019 12:05:42 +0000 (05:05 -0700)
See RFC: https://groups.google.com/a/tensorflow.org/forum/#!topic/mlir/xE2IzfhE3Wg.

Opaque location stores two pointers, one of them points to some data structure that is external to MLIR, and the other one is unique for each type and represents type id of that data structure. OpaqueLoc also stores an optional location that can be used if the first one is not suitable.
OpaqueLoc is managed similar to FileLineColLoc. It is passed around by MLIR transformations and can be used in compound locations like CallSiteLoc.

PiperOrigin-RevId: 273266510

mlir/g3doc/Diagnostics.md
mlir/include/mlir/IR/Attributes.h
mlir/include/mlir/IR/Location.h
mlir/lib/IR/AsmPrinter.cpp
mlir/lib/IR/Location.cpp
mlir/lib/IR/LocationDetail.h
mlir/lib/IR/MLIRContext.cpp
mlir/test/IR/opaque_locations.mlir [new file with mode: 0644]
mlir/test/lib/Transforms/TestOpaqueLoc.cpp [new file with mode: 0644]

index 67a6ae1..7589e78 100644 (file)
@@ -57,6 +57,13 @@ An instance of this location allows for attaching a name to a child location.
 This can be useful for representing the locations of variable, or node,
 definitions.
 
+### Opaque Location
+
+An instance of this location essentially contains a pointer to some data
+structure that is external to MLIR and an optional location that can be used if
+the first one is not suitable. Since it contains an external structure, only the
+optional location is used during serialization.
+
 ### Unknown Location
 
 ``` {.ebnf}
index ea33864..2d2d4f5 100644 (file)
@@ -162,6 +162,7 @@ enum Kind {
   FileLineColLocation,
   FusedLocation,
   NameLocation,
+  OpaqueLocation,
   UnknownLocation,
 
   // Represents a location as a 'void*' pointer to a front-end's opaque
index d7ad5f7..bb55ad6 100644 (file)
@@ -33,12 +33,13 @@ class Identifier;
 
 namespace detail {
 
-struct LocationStorage;
-struct UnknownLocationStorage;
-struct FileLineColLocationStorage;
-struct NameLocationStorage;
 struct CallSiteLocationStorage;
+struct FileLineColLocationStorage;
 struct FusedLocationStorage;
+struct LocationStorage;
+struct NameLocationStorage;
+struct OpaqueLocationStorage;
+struct UnknownLocationStorage;
 
 } // namespace detail
 
@@ -99,8 +100,8 @@ inline raw_ostream &operator<<(raw_ostream &os, const Location &loc) {
 }
 
 /// Represents a location as call site. "callee" is the concrete location
-/// (Unknown/NameLocation/FileLineColLoc) and "caller" points to the caller's
-/// location (another CallLocation or a concrete location). Multiple
+/// (Unknown/NameLocation/FileLineColLoc/OpaqueLoc) and "caller" points to the
+/// caller's location (another CallLocation or a concrete location). Multiple
 /// CallSiteLocs can be chained to form a call stack.
 class CallSiteLoc
     : public Attribute::AttrBase<CallSiteLoc, LocationAttr,
@@ -222,6 +223,77 @@ public:
   }
 };
 
+/// Represents a location that is external to MLIR. Contains a pointer to some
+/// data structure and an optional location that can be used if the first one is
+/// not suitable. Since it contains an external structure, only optional
+/// location is used during serialization.
+/// The class also provides a number of methods for making type-safe casts
+/// between a pointer to an object and opaque location.
+class OpaqueLoc : public Attribute::AttrBase<OpaqueLoc, LocationAttr,
+                                             detail::OpaqueLocationStorage> {
+public:
+  using Base::Base;
+
+  /// Returns an instance of opaque location which contains a given pointer to
+  /// an object. The corresponding MLIR location is set to UnknownLoc.
+  template <typename T>
+  static Location get(T underlyingLocation, MLIRContext *context) {
+    return get(reinterpret_cast<uintptr_t>(underlyingLocation),
+               ClassID::getID<T>(), UnknownLoc::get(context));
+  }
+
+  /// Returns an instance of opaque location which contains a given pointer to
+  /// an object and an additional MLIR location.
+  template <typename T>
+  static Location get(T underlyingLocation, Location fallbackLocation) {
+    return get(reinterpret_cast<uintptr_t>(underlyingLocation),
+               ClassID::getID<T>(), fallbackLocation);
+  }
+
+  /// Returns a pointer to some data structure that opaque location stores.
+  template <typename T> static T getUnderlyingLocation(Location location) {
+    assert(isa<T>(location));
+    return reinterpret_cast<T>(
+        location.cast<mlir::OpaqueLoc>().getUnderlyingLocation());
+  }
+
+  /// Returns a pointer to some data structure that opaque location stores.
+  /// Returns nullptr if provided location is not opaque location or if it
+  /// contains a pointer of different type.
+  template <typename T>
+  static T getUnderlyingLocationOrNull(Location location) {
+    return isa<T>(location)
+               ? reinterpret_cast<T>(
+                     location.cast<mlir::OpaqueLoc>().getUnderlyingLocation())
+               : T(nullptr);
+  }
+
+  /// Checks whether provided location is opaque location and contains a pointer
+  /// to an object of particular type.
+  template <typename T> static bool isa(Location location) {
+    auto opaque_loc = location.dyn_cast<OpaqueLoc>();
+    return opaque_loc && opaque_loc.getClassId() == ClassID::getID<T>();
+  }
+
+  /// Returns a pointer to the corresponding object.
+  uintptr_t getUnderlyingLocation() const;
+
+  /// Returns a ClassID* that represents the underlying objects c++ type.
+  ClassID *getClassId() const;
+
+  /// Returns a fallback location.
+  Location getFallbackLocation() const;
+
+  /// Methods for support type inquiry through isa, cast, and dyn_cast.
+  static bool kindof(unsigned kind) {
+    return kind == StandardAttributes::OpaqueLocation;
+  }
+
+private:
+  static Location get(uintptr_t underlyingLocation, ClassID *classID,
+                      Location fallbackLocation);
+};
+
 // Make Location hashable.
 inline ::llvm::hash_code hash_value(Location arg) {
   return hash_value(arg.impl);
index d9ca6f1..ce79db0 100644 (file)
@@ -386,6 +386,9 @@ void ModulePrinter::printTrailingLocation(Location loc) {
 
 void ModulePrinter::printLocationInternal(LocationAttr loc, bool pretty) {
   switch (loc.getKind()) {
+  case StandardAttributes::OpaqueLocation:
+    printLocationInternal(loc.cast<OpaqueLoc>().getFallbackLocation(), pretty);
+    break;
   case StandardAttributes::UnknownLocation:
     if (pretty)
       os << "[unknown]";
@@ -722,6 +725,7 @@ void ModulePrinter::printAttribute(Attribute attr, bool mayElideType) {
   case StandardAttributes::FileLineColLocation:
   case StandardAttributes::FusedLocation:
   case StandardAttributes::NameLocation:
+  case StandardAttributes::OpaqueLocation:
   case StandardAttributes::UnknownLocation:
     printLocation(attr.cast<LocationAttr>());
     break;
index 7be9280..1ea75d5 100644 (file)
@@ -123,3 +123,24 @@ Identifier NameLoc::getName() const { return getImpl()->name; }
 
 /// Return the child location.
 Location NameLoc::getChildLoc() const { return getImpl()->child; }
+
+//===----------------------------------------------------------------------===//
+// OpaqueLoc
+//===----------------------------------------------------------------------===//
+
+Location OpaqueLoc::get(uintptr_t underlyingLocation, ClassID *classID,
+                        Location fallbackLocation) {
+  return Base::get(fallbackLocation->getContext(),
+                   StandardAttributes::OpaqueLocation, underlyingLocation,
+                   classID, fallbackLocation);
+}
+
+uintptr_t OpaqueLoc::getUnderlyingLocation() const {
+  return Base::getImpl()->underlyingLocation;
+}
+
+ClassID *OpaqueLoc::getClassId() const { return getImpl()->classId; }
+
+Location OpaqueLoc::getFallbackLocation() const {
+  return Base::getImpl()->fallbackLocation;
+}
index 2076eb7..6ccaa17 100644 (file)
@@ -134,6 +134,37 @@ struct NameLocationStorage : public AttributeStorage {
   Location child;
 };
 
+struct OpaqueLocationStorage : public AttributeStorage {
+  OpaqueLocationStorage(uintptr_t underlyingLocation, ClassID *classId,
+                        Location fallbackLocation)
+      : underlyingLocation(underlyingLocation), classId(classId),
+        fallbackLocation(fallbackLocation) {}
+
+  /// The hash key used for uniquing.
+  using KeyTy = std::tuple<uintptr_t, ClassID *, Location>;
+  bool operator==(const KeyTy &key) const {
+    return key == KeyTy(underlyingLocation, classId, fallbackLocation);
+  }
+
+  /// Construct a new storage instance.
+  static OpaqueLocationStorage *construct(AttributeStorageAllocator &allocator,
+                                          const KeyTy &key) {
+    return new (allocator.allocate<OpaqueLocationStorage>())
+        OpaqueLocationStorage(std::get<0>(key), std::get<1>(key),
+                              std::get<2>(key));
+  }
+
+  /// Pointer to the corresponding object.
+  uintptr_t underlyingLocation;
+
+  /// A unique pointer for each type of underlyingLocation.
+  ClassID *classId;
+
+  /// An additional location that can be used if the external one is not
+  /// suitable.
+  Location fallbackLocation;
+};
+
 } // end namespace detail
 } // end namespace mlir
 
index d64d08a..c624a06 100644 (file)
@@ -86,7 +86,8 @@ struct BuiltinDialect : public Dialect {
                   DictionaryAttr, FloatAttr, SymbolRefAttr, IntegerAttr,
                   IntegerSetAttr, OpaqueAttr, OpaqueElementsAttr,
                   SparseElementsAttr, StringAttr, TypeAttr, UnitAttr>();
-    addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, UnknownLoc>();
+    addAttributes<CallSiteLoc, FileLineColLoc, FusedLoc, NameLoc, OpaqueLoc,
+                  UnknownLoc>();
 
     addTypes<ComplexType, FloatType, FunctionType, IndexType, IntegerType,
              MemRefType, NoneType, OpaqueType, RankedTensorType, TupleType,
diff --git a/mlir/test/IR/opaque_locations.mlir b/mlir/test/IR/opaque_locations.mlir
new file mode 100644 (file)
index 0000000..557534d
--- /dev/null
@@ -0,0 +1,35 @@
+// RUN: mlir-opt %s -test-opaque-loc -mlir-print-debuginfo | FileCheck %s
+// This test verifies that debug opaque locations can be printed.
+
+#set0 = (d0) : (1 == 0)
+
+// CHECK: MyLocation: 0: 'foo' op
+// CHECK: nullptr: 'foo' op
+// CHECK: MyLocation: 0: 'foo' op
+// CHECK: MyLocation: 1: 'std.constant' op
+// CHECK: nullptr: 'std.constant' op
+// CHECK: MyLocation: 1: 'std.constant' op
+
+// CHECK-LABEL: func @inline_notation
+func @inline_notation() -> i32 {
+  // CHECK: -> i32 loc("foo")
+  // CHECK: -> i32 loc("foo")
+  // CHECK: -> i32 loc(unknown)
+  %1 = "foo"() : () -> i32 loc("foo")
+
+  // CHECK: constant 4 : index loc(callsite("foo" at "mysource.cc":10:8))
+  // CHECK: constant 4 : index loc(callsite("foo" at "mysource.cc":10:8))
+  // CHECK: constant 4 : index loc(unknown)
+  %2 = constant 4 : index loc(callsite("foo" at "mysource.cc":10:8))
+
+  // CHECK: } loc(unknown)
+  affine.for %i0 = 0 to 8 {
+  } loc(fused["foo", "mysource.cc":10:8])
+
+  // CHECK: } loc(unknown)
+  affine.for %i0 = 0 to 8 {
+  } loc(fused["foo", "mysource.cc":10:8, callsite("foo" at "mysource.cc":10:8)])
+
+  // CHECK: return %{{.*}} : i32 loc(unknown)
+  return %1 : i32 loc(unknown)
+}
diff --git a/mlir/test/lib/Transforms/TestOpaqueLoc.cpp b/mlir/test/lib/Transforms/TestOpaqueLoc.cpp
new file mode 100644 (file)
index 0000000..0db5332
--- /dev/null
@@ -0,0 +1,93 @@
+//===- TestOpaqueLoc.cpp - Pass to test opaque locations ------------------===//
+//
+// Copyright 2019 The MLIR Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// =============================================================================
+
+#include "mlir/Dialect/StandardOps/Ops.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/Pass/Pass.h"
+
+using namespace mlir;
+
+namespace {
+/// Pass that changes locations to opaque locations for each operation.
+/// It also takes all operations that are not function operations or
+/// terminators and clones them with opaque locations which store the initial
+/// locations.
+struct TestOpaqueLoc : public ModulePass<TestOpaqueLoc> {
+
+  /// A simple structure which is used for testing as an underlying location in
+  /// OpaqueLoc.
+  struct MyLocation {
+    MyLocation() : id(42) {}
+    MyLocation(int id) : id(id) {}
+    int getId() { return id; }
+
+    int id;
+  };
+
+  void runOnModule() override {
+    std::vector<std::unique_ptr<MyLocation>> myLocs;
+    int last_it = 0;
+
+    getModule().walk([&](Operation *op) {
+      myLocs.push_back(std::make_unique<MyLocation>(last_it++));
+
+      Location loc = op->getLoc();
+
+      /// Set opaque location without fallback location to test the
+      /// corresponding get method.
+      op->setLoc(
+          OpaqueLoc::get<MyLocation *>(myLocs.back().get(), &getContext()));
+
+      if (isa<FuncOp>(op) || op->isKnownTerminator())
+        return;
+
+      OpBuilder builder(op);
+
+      /// Add the same operation but with fallback location to test the
+      /// corresponding get method and serialization.
+      Operation *op_cloned_1 = builder.clone(*op);
+      op_cloned_1->setLoc(
+          OpaqueLoc::get<MyLocation *>(myLocs.back().get(), loc));
+
+      /// Add the same operation but with void* instead of MyLocation* to test
+      /// getUnderlyingLocationOrNull method.
+      Operation *op_cloned_2 = builder.clone(*op);
+      op_cloned_2->setLoc(OpaqueLoc::get<void *>(nullptr, loc));
+    });
+
+    ScopedDiagnosticHandler diagHandler(&getContext(), [](Diagnostic &diag) {
+      auto &os = llvm::outs();
+      if (diag.getLocation().isa<OpaqueLoc>()) {
+        MyLocation *loc = OpaqueLoc::getUnderlyingLocationOrNull<MyLocation *>(
+            diag.getLocation());
+        if (loc)
+          os << "MyLocation: " << loc->id;
+        else
+          os << "nullptr";
+      }
+      os << ": " << diag << '\n';
+      os.flush();
+    });
+
+    getModule().walk([&](Operation *op) { op->emitOpError(); });
+  }
+};
+
+} // end anonymous namespace
+
+static PassRegistration<TestOpaqueLoc>
+    pass("test-opaque-loc", "Changes all leaf locations to opaque locations");