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}
FileLineColLocation,
FusedLocation,
NameLocation,
+ OpaqueLocation,
UnknownLocation,
// Represents a location as a 'void*' pointer to a front-end's opaque
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
}
/// 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,
}
};
+/// 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);
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]";
case StandardAttributes::FileLineColLocation:
case StandardAttributes::FusedLocation:
case StandardAttributes::NameLocation:
+ case StandardAttributes::OpaqueLocation:
case StandardAttributes::UnknownLocation:
printLocation(attr.cast<LocationAttr>());
break;
/// 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;
+}
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
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,
--- /dev/null
+// 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)
+}
--- /dev/null
+//===- 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");