MLIR  22.0.0git
PtrDialect.cpp
Go to the documentation of this file.
1 //===- PtrDialect.cpp - Pointer dialect ---------------------*- C++ -*-===//
2 //
3 // This file is licensed under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the Pointer dialect.
10 //
11 //===----------------------------------------------------------------------===//
12 
15 #include "mlir/IR/Matchers.h"
18 #include "llvm/ADT/TypeSwitch.h"
19 
20 using namespace mlir;
21 using namespace mlir::ptr;
22 
23 //===----------------------------------------------------------------------===//
24 // Pointer dialect
25 //===----------------------------------------------------------------------===//
26 
27 void PtrDialect::initialize() {
28  addOperations<
29 #define GET_OP_LIST
30 #include "mlir/Dialect/Ptr/IR/PtrOps.cpp.inc"
31  >();
32  addAttributes<
33 #define GET_ATTRDEF_LIST
34 #include "mlir/Dialect/Ptr/IR/PtrOpsAttrs.cpp.inc"
35  >();
36  addTypes<
37 #define GET_TYPEDEF_LIST
38 #include "mlir/Dialect/Ptr/IR/PtrOpsTypes.cpp.inc"
39  >();
40 }
41 
42 //===----------------------------------------------------------------------===//
43 // FromPtrOp
44 //===----------------------------------------------------------------------===//
45 
46 OpFoldResult FromPtrOp::fold(FoldAdaptor adaptor) {
47  // Fold the pattern:
48  // %ptr = ptr.to_ptr %v : type -> ptr
49  // (%mda = ptr.get_metadata %v : type)?
50  // %val = ptr.from_ptr %ptr (metadata %mda)? : ptr -> type
51  // To:
52  // %val -> %v
53  Value ptrLike;
54  FromPtrOp fromPtr = *this;
55  while (fromPtr != nullptr) {
56  auto toPtr = fromPtr.getPtr().getDefiningOp<ToPtrOp>();
57  // Cannot fold if it's not a `to_ptr` op or the initial and final types are
58  // different.
59  if (!toPtr || toPtr.getPtr().getType() != fromPtr.getType())
60  return ptrLike;
61  Value md = fromPtr.getMetadata();
62  // If the type has trivial metadata fold.
63  if (!fromPtr.getType().hasPtrMetadata()) {
64  ptrLike = toPtr.getPtr();
65  } else if (md) {
66  // Fold if the metadata can be verified to be equal.
67  if (auto mdOp = md.getDefiningOp<GetMetadataOp>();
68  mdOp && mdOp.getPtr() == toPtr.getPtr())
69  ptrLike = toPtr.getPtr();
70  }
71  // Check for a sequence of casts.
72  fromPtr = ptrLike ? ptrLike.getDefiningOp<FromPtrOp>() : nullptr;
73  }
74  return ptrLike;
75 }
76 
77 LogicalResult FromPtrOp::verify() {
78  if (isa<PtrType>(getType()))
79  return emitError() << "the result type cannot be `!ptr.ptr`";
80  if (getType().getMemorySpace() != getPtr().getType().getMemorySpace()) {
81  return emitError()
82  << "expected the input and output to have the same memory space";
83  }
84  return success();
85 }
86 
87 //===----------------------------------------------------------------------===//
88 // LoadOp
89 //===----------------------------------------------------------------------===//
90 
91 /// Verifies the attributes and the type of atomic memory access operations.
92 template <typename OpTy>
93 static LogicalResult
94 verifyAtomicMemOp(OpTy memOp, ArrayRef<AtomicOrdering> unsupportedOrderings) {
95  if (memOp.getOrdering() != AtomicOrdering::not_atomic) {
96  if (llvm::is_contained(unsupportedOrderings, memOp.getOrdering()))
97  return memOp.emitOpError("unsupported ordering '")
98  << stringifyAtomicOrdering(memOp.getOrdering()) << "'";
99  if (!memOp.getAlignment())
100  return memOp.emitOpError("expected alignment for atomic access");
101  return success();
102  }
103  if (memOp.getSyncscope()) {
104  return memOp.emitOpError(
105  "expected syncscope to be null for non-atomic access");
106  }
107  return success();
108 }
109 
110 /// Verifies that the alignment attribute is a power of 2 if present.
111 static LogicalResult
112 verifyAlignment(std::optional<int64_t> alignment,
114  if (!alignment)
115  return success();
116  if (alignment.value() <= 0)
117  return emitError() << "alignment must be positive";
118  if (!llvm::isPowerOf2_64(alignment.value()))
119  return emitError() << "alignment must be a power of 2";
120  return success();
121 }
122 
123 void LoadOp::getEffects(
125  &effects) {
126  effects.emplace_back(MemoryEffects::Read::get(), &getPtrMutable());
127  // Volatile operations can have target-specific read-write effects on
128  // memory besides the one referred to by the pointer operand.
129  // Similarly, atomic operations that are monotonic or stricter cause
130  // synchronization that from a language point-of-view, are arbitrary
131  // read-writes into memory.
132  if (getVolatile_() || (getOrdering() != AtomicOrdering::not_atomic &&
133  getOrdering() != AtomicOrdering::unordered)) {
134  effects.emplace_back(MemoryEffects::Write::get());
135  effects.emplace_back(MemoryEffects::Read::get());
136  }
137 }
138 
139 LogicalResult LoadOp::verify() {
140  auto emitDiag = [&]() -> InFlightDiagnostic { return emitError(); };
141  MemorySpaceAttrInterface ms = getPtr().getType().getMemorySpace();
142  if (!ms.isValidLoad(getResult().getType(), getOrdering(), getAlignment(),
143  emitDiag))
144  return failure();
145  if (failed(verifyAlignment(getAlignment(), emitDiag)))
146  return failure();
147  return verifyAtomicMemOp(*this,
148  {AtomicOrdering::release, AtomicOrdering::acq_rel});
149 }
150 
151 void LoadOp::build(OpBuilder &builder, OperationState &state, Type type,
152  Value addr, unsigned alignment, bool isVolatile,
153  bool isNonTemporal, bool isInvariant, bool isInvariantGroup,
154  AtomicOrdering ordering, StringRef syncscope) {
155  build(builder, state, type, addr,
156  alignment ? std::optional<int64_t>(alignment) : std::nullopt,
157  isVolatile, isNonTemporal, isInvariant, isInvariantGroup, ordering,
158  syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
159 }
160 
161 //===----------------------------------------------------------------------===//
162 // StoreOp
163 //===----------------------------------------------------------------------===//
164 
165 void StoreOp::getEffects(
167  &effects) {
168  effects.emplace_back(MemoryEffects::Write::get(), &getPtrMutable());
169  // Volatile operations can have target-specific read-write effects on
170  // memory besides the one referred to by the pointer operand.
171  // Similarly, atomic operations that are monotonic or stricter cause
172  // synchronization that from a language point-of-view, are arbitrary
173  // read-writes into memory.
174  if (getVolatile_() || (getOrdering() != AtomicOrdering::not_atomic &&
175  getOrdering() != AtomicOrdering::unordered)) {
176  effects.emplace_back(MemoryEffects::Write::get());
177  effects.emplace_back(MemoryEffects::Read::get());
178  }
179 }
180 
181 LogicalResult StoreOp::verify() {
182  auto emitDiag = [&]() -> InFlightDiagnostic { return emitError(); };
183  MemorySpaceAttrInterface ms = getPtr().getType().getMemorySpace();
184  if (!ms.isValidStore(getValue().getType(), getOrdering(), getAlignment(),
185  emitDiag))
186  return failure();
187  if (failed(verifyAlignment(getAlignment(), emitDiag)))
188  return failure();
189  return verifyAtomicMemOp(*this,
190  {AtomicOrdering::acquire, AtomicOrdering::acq_rel});
191 }
192 
193 void StoreOp::build(OpBuilder &builder, OperationState &state, Value value,
194  Value addr, unsigned alignment, bool isVolatile,
195  bool isNonTemporal, bool isInvariantGroup,
196  AtomicOrdering ordering, StringRef syncscope) {
197  build(builder, state, value, addr,
198  alignment ? std::optional<int64_t>(alignment) : std::nullopt,
199  isVolatile, isNonTemporal, isInvariantGroup, ordering,
200  syncscope.empty() ? nullptr : builder.getStringAttr(syncscope));
201 }
202 
203 //===----------------------------------------------------------------------===//
204 // PtrAddOp
205 //===----------------------------------------------------------------------===//
206 
207 /// Fold: ptradd ptr + 0 -> ptr
208 OpFoldResult PtrAddOp::fold(FoldAdaptor adaptor) {
209  Attribute attr = adaptor.getOffset();
210  if (!attr)
211  return nullptr;
212  if (llvm::APInt value; m_ConstantInt(&value).match(attr) && value.isZero())
213  return getBase();
214  return nullptr;
215 }
216 
217 //===----------------------------------------------------------------------===//
218 // ToPtrOp
219 //===----------------------------------------------------------------------===//
220 
221 OpFoldResult ToPtrOp::fold(FoldAdaptor adaptor) {
222  // Fold the pattern:
223  // %val = ptr.from_ptr %p (metadata ...)? : ptr -> type
224  // %ptr = ptr.to_ptr %val : type -> ptr
225  // To:
226  // %ptr -> %p
227  Value ptr;
228  ToPtrOp toPtr = *this;
229  while (toPtr != nullptr) {
230  auto fromPtr = toPtr.getPtr().getDefiningOp<FromPtrOp>();
231  // Cannot fold if it's not a `from_ptr` op.
232  if (!fromPtr)
233  return ptr;
234  ptr = fromPtr.getPtr();
235  // Check for chains of casts.
236  toPtr = ptr.getDefiningOp<ToPtrOp>();
237  }
238  return ptr;
239 }
240 
241 LogicalResult ToPtrOp::verify() {
242  if (isa<PtrType>(getPtr().getType()))
243  return emitError() << "the input value cannot be of type `!ptr.ptr`";
244  if (getType().getMemorySpace() != getPtr().getType().getMemorySpace()) {
245  return emitError()
246  << "expected the input and output to have the same memory space";
247  }
248  return success();
249 }
250 
251 //===----------------------------------------------------------------------===//
252 // TypeOffsetOp
253 //===----------------------------------------------------------------------===//
254 
255 llvm::TypeSize TypeOffsetOp::getTypeSize(std::optional<DataLayout> layout) {
256  if (layout)
257  return layout->getTypeSize(getElementType());
258  DataLayout dl = DataLayout::closest(*this);
259  return dl.getTypeSize(getElementType());
260 }
261 
262 //===----------------------------------------------------------------------===//
263 // Pointer API.
264 //===----------------------------------------------------------------------===//
265 
266 #include "mlir/Dialect/Ptr/IR/PtrOpsDialect.cpp.inc"
267 
268 #define GET_ATTRDEF_CLASSES
269 #include "mlir/Dialect/Ptr/IR/PtrOpsAttrs.cpp.inc"
270 
271 #include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.cpp.inc"
272 
273 #include "mlir/Dialect/Ptr/IR/MemorySpaceAttrInterfaces.cpp.inc"
274 
275 #include "mlir/Dialect/Ptr/IR/PtrOpsEnums.cpp.inc"
276 
277 #define GET_TYPEDEF_CLASSES
278 #include "mlir/Dialect/Ptr/IR/PtrOpsTypes.cpp.inc"
279 
280 #define GET_OP_CLASSES
281 #include "mlir/Dialect/Ptr/IR/PtrOps.cpp.inc"
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static InFlightDiagnostic emitDiag(Location location, DiagnosticSeverity severity, const Twine &message)
Helper function used to emit a diagnostic with an optionally empty twine message.
static Type getElementType(Type type)
Determine the element type of type.
static LogicalResult verifyAtomicMemOp(OpTy memOp, ArrayRef< AtomicOrdering > unsupportedOrderings)
Verifies the attributes and the type of atomic memory access operations.
Definition: PtrDialect.cpp:94
static LogicalResult verifyAlignment(std::optional< int64_t > alignment, function_ref< InFlightDiagnostic()> emitError)
Verifies that the alignment attribute is a power of 2 if present.
Definition: PtrDialect.cpp:112
Attributes are known-constant values of operations.
Definition: Attributes.h:25
StringAttr getStringAttr(const Twine &bytes)
Definition: Builders.cpp:257
The main mechanism for performing data layout queries.
static DataLayout closest(Operation *op)
Returns the layout of the closest parent operation carrying layout info.
llvm::TypeSize getTypeSize(Type t) const
Returns the size of the given type in the current scope.
This class represents a diagnostic that is inflight and set to be reported.
Definition: Diagnostics.h:314
This class helps build Operations.
Definition: Builders.h:205
This class represents a single result from folding an operation.
Definition: OpDefinition.h:272
This class represents a specific instance of an effect.
static DerivedEffect * get()
Returns a unique instance for the derived effect class.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition: Types.h:74
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition: Value.h:96
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:18
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition: Remarks.h:491
Include the generated interface declarations.
detail::constant_int_value_binder m_ConstantInt(IntegerAttr::ValueType *bind_value)
Matches a constant holding a scalar/vector/tensor integer (splat) and writes the integer value to bin...
Definition: Matchers.h:527
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition: Utils.cpp:304
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
LogicalResult verify(Operation *op, bool verifyRecursively=true)
Perform (potentially expensive) checks of invariants, used to detect compiler bugs,...
Definition: Verifier.cpp:423
This represents an operation in an abstracted form, suitable for use with the builder APIs.