MLIR  19.0.0git
TypeConsistency.cpp
Go to the documentation of this file.
1 //===- TypeConsistency.cpp - Rewrites to improve type consistency ---------===//
2 //
3 // Part of the LLVM Project, 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 
11 #include "llvm/ADT/TypeSwitch.h"
12 
13 namespace mlir {
14 namespace LLVM {
15 #define GEN_PASS_DEF_LLVMTYPECONSISTENCY
16 #include "mlir/Dialect/LLVMIR/Transforms/Passes.h.inc"
17 } // namespace LLVM
18 } // namespace mlir
19 
20 using namespace mlir;
21 using namespace LLVM;
22 
23 //===----------------------------------------------------------------------===//
24 // Utils
25 //===----------------------------------------------------------------------===//
26 
27 /// Checks that a pointer value has a pointee type hint consistent with the
28 /// expected type. Returns the type it actually hints to if it differs, or
29 /// nullptr if the type is consistent or impossible to analyze.
30 static Type isElementTypeInconsistent(Value addr, Type expectedType) {
31  auto defOp = dyn_cast_or_null<GetResultPtrElementType>(addr.getDefiningOp());
32  if (!defOp)
33  return nullptr;
34 
35  Type elemType = defOp.getResultPtrElementType();
36  if (!elemType)
37  return nullptr;
38 
39  if (elemType == expectedType)
40  return nullptr;
41 
42  return elemType;
43 }
44 
45 //===----------------------------------------------------------------------===//
46 // CanonicalizeAlignedGep
47 //===----------------------------------------------------------------------===//
48 
49 /// Returns the amount of bytes the provided GEP elements will offset the
50 /// pointer by. Returns nullopt if the offset could not be computed.
51 static std::optional<uint64_t> gepToByteOffset(DataLayout &layout, GEPOp gep) {
52 
53  SmallVector<uint32_t> indices;
54  // Ensures all indices are static and fetches them.
55  for (auto index : gep.getIndices()) {
56  IntegerAttr indexInt = llvm::dyn_cast_if_present<IntegerAttr>(index);
57  if (!indexInt)
58  return std::nullopt;
59  int32_t gepIndex = indexInt.getInt();
60  if (gepIndex < 0)
61  return std::nullopt;
62  indices.push_back(static_cast<uint32_t>(gepIndex));
63  }
64 
65  uint64_t offset = indices[0] * layout.getTypeSize(gep.getElemType());
66 
67  Type currentType = gep.getElemType();
68  for (uint32_t index : llvm::drop_begin(indices)) {
69  bool shouldCancel =
70  TypeSwitch<Type, bool>(currentType)
71  .Case([&](LLVMArrayType arrayType) {
72  if (arrayType.getNumElements() <= index)
73  return true;
74  offset += index * layout.getTypeSize(arrayType.getElementType());
75  currentType = arrayType.getElementType();
76  return false;
77  })
78  .Case([&](LLVMStructType structType) {
79  ArrayRef<Type> body = structType.getBody();
80  if (body.size() <= index)
81  return true;
82  for (uint32_t i = 0; i < index; i++) {
83  if (!structType.isPacked())
84  offset = llvm::alignTo(offset,
85  layout.getTypeABIAlignment(body[i]));
86  offset += layout.getTypeSize(body[i]);
87  }
88  currentType = body[index];
89  return false;
90  })
91  .Default([](Type) { return true; });
92 
93  if (shouldCancel)
94  return std::nullopt;
95  }
96 
97  return offset;
98 }
99 
100 /// Fills in `equivalentIndicesOut` with GEP indices that would be equivalent to
101 /// offsetting a pointer by `offset` bytes, assuming the GEP has `base` as base
102 /// type.
103 static LogicalResult
104 findIndicesForOffset(DataLayout &layout, Type base, uint64_t offset,
105  SmallVectorImpl<GEPArg> &equivalentIndicesOut) {
106 
107  uint64_t baseSize = layout.getTypeSize(base);
108  uint64_t rootIndex = offset / baseSize;
109  if (rootIndex > std::numeric_limits<uint32_t>::max())
110  return failure();
111  equivalentIndicesOut.push_back(rootIndex);
112 
113  uint64_t distanceToStart = rootIndex * baseSize;
114 
115 #ifndef NDEBUG
116  auto isWithinCurrentType = [&](Type currentType) {
117  return offset < distanceToStart + layout.getTypeSize(currentType);
118  };
119 #endif
120 
121  Type currentType = base;
122  while (distanceToStart < offset) {
123  // While an index that does not perfectly align with offset has not been
124  // reached...
125 
126  assert(isWithinCurrentType(currentType));
127 
128  bool shouldCancel =
129  TypeSwitch<Type, bool>(currentType)
130  .Case([&](LLVMArrayType arrayType) {
131  // Find which element of the array contains the offset.
132  uint64_t elemSize =
133  layout.getTypeSize(arrayType.getElementType());
134  uint64_t index = (offset - distanceToStart) / elemSize;
135  equivalentIndicesOut.push_back(index);
136  distanceToStart += index * elemSize;
137 
138  // Then, try to find where in the element the offset is. If the
139  // offset is exactly the beginning of the element, the loop is
140  // complete.
141  currentType = arrayType.getElementType();
142 
143  // Only continue if the element in question can be indexed using
144  // an i32.
145  return index > std::numeric_limits<uint32_t>::max();
146  })
147  .Case([&](LLVMStructType structType) {
148  ArrayRef<Type> body = structType.getBody();
149  uint32_t index = 0;
150 
151  // Walk over the elements of the struct to find in which of them
152  // the offset is.
153  for (Type elem : body) {
154  uint64_t elemSize = layout.getTypeSize(elem);
155  if (!structType.isPacked()) {
156  distanceToStart = llvm::alignTo(
157  distanceToStart, layout.getTypeABIAlignment(elem));
158  // If the offset is in padding, cancel the rewrite.
159  if (offset < distanceToStart)
160  return true;
161  }
162 
163  if (offset < distanceToStart + elemSize) {
164  // The offset is within this element, stop iterating the
165  // struct and look within the current element.
166  equivalentIndicesOut.push_back(index);
167  currentType = elem;
168  return false;
169  }
170 
171  // The offset is not within this element, continue walking over
172  // the struct.
173  distanceToStart += elemSize;
174  index++;
175  }
176 
177  // The offset was supposed to be within this struct but is not.
178  // This can happen if the offset points into final padding.
179  // Anyway, nothing can be done.
180  return true;
181  })
182  .Default([](Type) {
183  // If the offset is within a type that cannot be split, no indices
184  // will yield this offset. This can happen if the offset is not
185  // perfectly aligned with a leaf type.
186  // TODO: support vectors.
187  return true;
188  });
189 
190  if (shouldCancel)
191  return failure();
192  }
193 
194  return success();
195 }
196 
197 /// Returns the consistent type for the GEP if the GEP is not type-consistent.
198 /// Returns failure if the GEP is already consistent.
200  // GEP of typed pointers are not supported.
201  if (!gep.getElemType())
202  return failure();
203 
204  std::optional<Type> maybeBaseType = gep.getElemType();
205  if (!maybeBaseType)
206  return failure();
207  Type baseType = *maybeBaseType;
208 
209  Type typeHint = isElementTypeInconsistent(gep.getBase(), baseType);
210  if (!typeHint)
211  return failure();
212  return typeHint;
213 }
214 
217  PatternRewriter &rewriter) const {
219  if (failed(typeHint)) {
220  // GEP is already canonical, nothing to do here.
221  return failure();
222  }
223 
224  DataLayout layout = DataLayout::closest(gep);
225  std::optional<uint64_t> desiredOffset = gepToByteOffset(layout, gep);
226  if (!desiredOffset)
227  return failure();
228 
229  SmallVector<GEPArg> newIndices;
230  if (failed(
231  findIndicesForOffset(layout, *typeHint, *desiredOffset, newIndices)))
232  return failure();
233 
234  rewriter.replaceOpWithNewOp<GEPOp>(
235  gep, LLVM::LLVMPointerType::get(getContext()), *typeHint, gep.getBase(),
236  newIndices, gep.getInbounds());
237 
238  return success();
239 }
240 
241 namespace {
242 /// Class abstracting over both array and struct types, turning each into ranges
243 /// of their sub-types.
244 class DestructurableTypeRange
245  : public llvm::indexed_accessor_range<DestructurableTypeRange,
246  DestructurableTypeInterface, Type,
247  Type *, Type> {
248 
249  using Base = llvm::indexed_accessor_range<
250  DestructurableTypeRange, DestructurableTypeInterface, Type, Type *, Type>;
251 
252 public:
253  using Base::Base;
254 
255  /// Constructs a DestructurableTypeRange from either a LLVMStructType or
256  /// LLVMArrayType.
257  explicit DestructurableTypeRange(DestructurableTypeInterface base)
258  : Base(base, 0, [&]() -> ptrdiff_t {
259  return TypeSwitch<DestructurableTypeInterface, ptrdiff_t>(base)
260  .Case([](LLVMStructType structType) {
261  return structType.getBody().size();
262  })
263  .Case([](LLVMArrayType arrayType) {
264  return arrayType.getNumElements();
265  })
266  .Default([](auto) -> ptrdiff_t {
267  llvm_unreachable(
268  "Only LLVMStructType or LLVMArrayType supported");
269  });
270  }()) {}
271 
272  /// Returns true if this is a range over a packed struct.
273  bool isPacked() const {
274  if (auto structType = dyn_cast<LLVMStructType>(getBase()))
275  return structType.isPacked();
276  return false;
277  }
278 
279 private:
280  static Type dereference(DestructurableTypeInterface base, ptrdiff_t index) {
281  // i32 chosen because the implementations of ArrayType and StructType
282  // specifically expect it to be 32 bit. They will fail otherwise.
283  Type result = base.getTypeAtIndex(
284  IntegerAttr::get(IntegerType::get(base.getContext(), 32), index));
285  assert(result && "Should always succeed");
286  return result;
287  }
288 
289  friend Base;
290 };
291 } // namespace
292 
293 /// Returns the list of elements of `destructurableType` that are written to by
294 /// a store operation writing `storeSize` bytes at `storeOffset`.
295 /// `storeOffset` is required to cleanly point to an immediate element within
296 /// the type. If the write operation were to write to any padding, write beyond
297 /// the aggregate or partially write to a non-aggregate, failure is returned.
299 getWrittenToFields(const DataLayout &dataLayout,
300  DestructurableTypeInterface destructurableType,
301  unsigned storeSize, unsigned storeOffset) {
302  DestructurableTypeRange destructurableTypeRange(destructurableType);
303 
304  unsigned currentOffset = 0;
305  for (; !destructurableTypeRange.empty();
306  destructurableTypeRange = destructurableTypeRange.drop_front()) {
307  Type type = destructurableTypeRange.front();
308  if (!destructurableTypeRange.isPacked()) {
309  unsigned alignment = dataLayout.getTypeABIAlignment(type);
310  currentOffset = llvm::alignTo(currentOffset, alignment);
311  }
312 
313  // currentOffset is guaranteed to be equal to offset since offset is either
314  // 0 or stems from a type-consistent GEP indexing into just a single
315  // aggregate.
316  if (currentOffset == storeOffset)
317  break;
318 
319  assert(currentOffset < storeOffset &&
320  "storeOffset should cleanly point into an immediate field");
321 
322  currentOffset += dataLayout.getTypeSize(type);
323  }
324 
325  size_t exclusiveEnd = 0;
326  for (; exclusiveEnd < destructurableTypeRange.size() && storeSize > 0;
327  exclusiveEnd++) {
328  if (!destructurableTypeRange.isPacked()) {
329  unsigned alignment =
330  dataLayout.getTypeABIAlignment(destructurableTypeRange[exclusiveEnd]);
331  // No padding allowed inbetween fields at this point in time.
332  if (!llvm::isAligned(llvm::Align(alignment), currentOffset))
333  return failure();
334  }
335 
336  unsigned fieldSize =
337  dataLayout.getTypeSize(destructurableTypeRange[exclusiveEnd]);
338  if (fieldSize > storeSize) {
339  // Partial writes into an aggregate are okay since subsequent pattern
340  // applications can further split these up into writes into the
341  // sub-elements.
342  auto subAggregate = dyn_cast<DestructurableTypeInterface>(
343  destructurableTypeRange[exclusiveEnd]);
344  if (!subAggregate)
345  return failure();
346 
347  // Avoid splitting redundantly by making sure the store into the
348  // aggregate can actually be split.
349  if (failed(getWrittenToFields(dataLayout, subAggregate, storeSize,
350  /*storeOffset=*/0)))
351  return failure();
352 
353  return destructurableTypeRange.take_front(exclusiveEnd + 1);
354  }
355  currentOffset += fieldSize;
356  storeSize -= fieldSize;
357  }
358 
359  // If the storeSize is not 0 at this point we are writing past the aggregate
360  // as a whole. Abort.
361  if (storeSize > 0)
362  return failure();
363  return destructurableTypeRange.take_front(exclusiveEnd);
364 }
365 
366 /// Splits a store of the vector `value` into `address` at `storeOffset` into
367 /// multiple stores of each element with the goal of each generated store
368 /// becoming type-consistent through subsequent pattern applications.
369 static void splitVectorStore(const DataLayout &dataLayout, Location loc,
370  RewriterBase &rewriter, Value address,
372  unsigned storeOffset) {
373  VectorType vectorType = value.getType();
374  unsigned elementSize = dataLayout.getTypeSize(vectorType.getElementType());
375 
376  // Extract every element in the vector and store it in the given address.
377  for (size_t index : llvm::seq<size_t>(0, vectorType.getNumElements())) {
378  auto pos =
379  rewriter.create<ConstantOp>(loc, rewriter.getI32IntegerAttr(index));
380  auto extractOp = rewriter.create<ExtractElementOp>(loc, value, pos);
381 
382  // For convenience, we do indexing by calculating the final byte offset.
383  // Other patterns will turn this into a type-consistent GEP.
384  auto gepOp = rewriter.create<GEPOp>(
385  loc, address.getType(), rewriter.getI8Type(), address,
387  static_cast<int32_t>(storeOffset + index * elementSize)});
388 
389  rewriter.create<StoreOp>(loc, extractOp, gepOp);
390  }
391 }
392 
393 /// Splits a store of the integer `value` into `address` at `storeOffset` into
394 /// multiple stores to each 'writtenToFields', making each store operation
395 /// type-consistent.
396 static void splitIntegerStore(const DataLayout &dataLayout, Location loc,
397  RewriterBase &rewriter, Value address,
398  Value value, unsigned storeSize,
399  unsigned storeOffset,
400  DestructurableTypeRange writtenToFields) {
401  unsigned currentOffset = storeOffset;
402  for (Type type : writtenToFields) {
403  unsigned fieldSize = dataLayout.getTypeSize(type);
404 
405  // Extract the data out of the integer by first shifting right and then
406  // truncating it.
407  auto pos = rewriter.create<ConstantOp>(
408  loc, rewriter.getIntegerAttr(value.getType(),
409  (currentOffset - storeOffset) * 8));
410 
411  auto shrOp = rewriter.create<LShrOp>(loc, value, pos);
412 
413  // If we are doing a partial write into a direct field the remaining
414  // `storeSize` will be less than the size of the field. We have to truncate
415  // to the `storeSize` to avoid creating a store that wasn't in the original
416  // code.
417  IntegerType fieldIntType =
418  rewriter.getIntegerType(std::min(fieldSize, storeSize) * 8);
419  Value valueToStore = rewriter.create<TruncOp>(loc, fieldIntType, shrOp);
420 
421  // We create an `i8` indexed GEP here as that is the easiest (offset is
422  // already known). Other patterns turn this into a type-consistent GEP.
423  auto gepOp = rewriter.create<GEPOp>(
424  loc, address.getType(), rewriter.getI8Type(), address,
425  ArrayRef<GEPArg>{static_cast<int32_t>(currentOffset)});
426  rewriter.create<StoreOp>(loc, valueToStore, gepOp);
427 
428  // No need to care about padding here since we already checked previously
429  // that no padding exists in this range.
430  currentOffset += fieldSize;
431  storeSize -= fieldSize;
432  }
433 }
434 
435 LogicalResult SplitStores::matchAndRewrite(StoreOp store,
436  PatternRewriter &rewriter) const {
437  Type sourceType = store.getValue().getType();
438  if (!isa<IntegerType, VectorType>(sourceType)) {
439  // We currently only support integer and vector sources.
440  return failure();
441  }
442 
443  Type typeHint = isElementTypeInconsistent(store.getAddr(), sourceType);
444  if (!typeHint) {
445  // Nothing to do, since it is already consistent.
446  return failure();
447  }
448 
449  auto dataLayout = DataLayout::closest(store);
450 
451  unsigned storeSize = dataLayout.getTypeSize(sourceType);
452  unsigned offset = 0;
453  Value address = store.getAddr();
454  if (auto gepOp = address.getDefiningOp<GEPOp>()) {
455  // Currently only handle canonical GEPs with exactly two indices,
456  // indexing a single aggregate deep.
457  // If the GEP is not canonical we have to fail, otherwise we would not
458  // create type-consistent IR.
459  if (gepOp.getIndices().size() != 2 ||
461  return failure();
462 
463  // If the size of the element indexed by the GEP is smaller than the store
464  // size, it is pointing into the middle of an aggregate with the store
465  // storing into multiple adjacent elements. Destructure into the base
466  // address of the aggregate with a store offset.
467  if (storeSize > dataLayout.getTypeSize(gepOp.getResultPtrElementType())) {
468  std::optional<uint64_t> byteOffset = gepToByteOffset(dataLayout, gepOp);
469  if (!byteOffset)
470  return failure();
471 
472  offset = *byteOffset;
473  typeHint = gepOp.getElemType();
474  address = gepOp.getBase();
475  }
476  }
477 
478  auto destructurableType = dyn_cast<DestructurableTypeInterface>(typeHint);
479  if (!destructurableType)
480  return failure();
481 
482  FailureOr<DestructurableTypeRange> writtenToElements =
483  getWrittenToFields(dataLayout, destructurableType, storeSize, offset);
484  if (failed(writtenToElements))
485  return failure();
486 
487  if (writtenToElements->size() <= 1) {
488  // Other patterns should take care of this case, we are only interested in
489  // splitting element stores.
490  return failure();
491  }
492 
493  if (isa<IntegerType>(sourceType)) {
494  splitIntegerStore(dataLayout, store.getLoc(), rewriter, address,
495  store.getValue(), storeSize, offset, *writtenToElements);
496  rewriter.eraseOp(store);
497  return success();
498  }
499 
500  // Add a reasonable bound to not split very large vectors that would end up
501  // generating lots of code.
502  if (dataLayout.getTypeSizeInBits(sourceType) > maxVectorSplitSize)
503  return failure();
504 
505  // Vector types are simply split into its elements and new stores generated
506  // with those. Subsequent pattern applications will split these stores further
507  // if required.
508  splitVectorStore(dataLayout, store.getLoc(), rewriter, address,
509  cast<TypedValue<VectorType>>(store.getValue()), offset);
510  rewriter.eraseOp(store);
511  return success();
512 }
513 
514 LogicalResult SplitGEP::matchAndRewrite(GEPOp gepOp,
515  PatternRewriter &rewriter) const {
517  if (succeeded(typeHint) || gepOp.getIndices().size() <= 2) {
518  // GEP is not canonical or a single aggregate deep, nothing to do here.
519  return failure();
520  }
521 
522  auto indexToGEPArg =
524  if (auto integerAttr = dyn_cast<IntegerAttr>(index))
525  return integerAttr.getValue().getSExtValue();
526  return cast<Value>(index);
527  };
528 
529  GEPIndicesAdaptor<ValueRange> indices = gepOp.getIndices();
530 
531  auto splitIter = std::next(indices.begin(), 2);
532 
533  // Split of the first GEP using the first two indices.
534  auto subGepOp = rewriter.create<GEPOp>(
535  gepOp.getLoc(), gepOp.getType(), gepOp.getElemType(), gepOp.getBase(),
536  llvm::map_to_vector(llvm::make_range(indices.begin(), splitIter),
537  indexToGEPArg),
538  gepOp.getInbounds());
539 
540  // The second GEP indexes on the result pointer element type of the previous
541  // with all the remaining indices and a zero upfront. If this GEP has more
542  // than two indices remaining it'll be further split in subsequent pattern
543  // applications.
544  SmallVector<GEPArg> newIndices = {0};
545  llvm::transform(llvm::make_range(splitIter, indices.end()),
546  std::back_inserter(newIndices), indexToGEPArg);
547  rewriter.replaceOpWithNewOp<GEPOp>(gepOp, gepOp.getType(),
548  subGepOp.getResultPtrElementType(),
549  subGepOp, newIndices, gepOp.getInbounds());
550  return success();
551 }
552 
553 //===----------------------------------------------------------------------===//
554 // Type consistency pass
555 //===----------------------------------------------------------------------===//
556 
557 namespace {
558 struct LLVMTypeConsistencyPass
559  : public LLVM::impl::LLVMTypeConsistencyBase<LLVMTypeConsistencyPass> {
560  void runOnOperation() override {
561  RewritePatternSet rewritePatterns(&getContext());
562  rewritePatterns.add<CanonicalizeAlignedGep>(&getContext());
563  rewritePatterns.add<SplitStores>(&getContext(), maxVectorSplitSize);
564  rewritePatterns.add<SplitGEP>(&getContext());
565  FrozenRewritePatternSet frozen(std::move(rewritePatterns));
566 
567  if (failed(applyPatternsAndFoldGreedily(getOperation(), frozen)))
568  signalPassFailure();
569  }
570 };
571 } // namespace
572 
573 std::unique_ptr<Pass> LLVM::createTypeConsistencyPass() {
574  return std::make_unique<LLVMTypeConsistencyPass>();
575 }
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static MLIRContext * getContext(OpFoldResult val)
static Value max(ImplicitLocOpBuilder &builder, Value value, Value bound)
static Value min(ImplicitLocOpBuilder &builder, Value value, Value bound)
static LogicalResult findIndicesForOffset(DataLayout &layout, Type base, uint64_t offset, SmallVectorImpl< GEPArg > &equivalentIndicesOut)
Fills in equivalentIndicesOut with GEP indices that would be equivalent to offsetting a pointer by of...
static Type isElementTypeInconsistent(Value addr, Type expectedType)
Checks that a pointer value has a pointee type hint consistent with the expected type.
static void splitIntegerStore(const DataLayout &dataLayout, Location loc, RewriterBase &rewriter, Value address, Value value, unsigned storeSize, unsigned storeOffset, DestructurableTypeRange writtenToFields)
Splits a store of the integer value into address at storeOffset into multiple stores to each 'written...
static std::optional< uint64_t > gepToByteOffset(DataLayout &layout, GEPOp gep)
Returns the amount of bytes the provided GEP elements will offset the pointer by.
static FailureOr< Type > getRequiredConsistentGEPType(GEPOp gep)
Returns the consistent type for the GEP if the GEP is not type-consistent.
static FailureOr< DestructurableTypeRange > getWrittenToFields(const DataLayout &dataLayout, DestructurableTypeInterface destructurableType, unsigned storeSize, unsigned storeOffset)
Returns the list of elements of destructurableType that are written to by a store operation writing s...
static void splitVectorStore(const DataLayout &dataLayout, Location loc, RewriterBase &rewriter, Value address, TypedValue< VectorType > value, unsigned storeOffset)
Splits a store of the vector value into address at storeOffset into multiple stores of each element w...
IntegerAttr getI32IntegerAttr(int32_t value)
Definition: Builders.cpp:216
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition: Builders.cpp:238
IntegerType getIntegerType(unsigned width)
Definition: Builders.cpp:87
IntegerType getI8Type()
Definition: Builders.cpp:79
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.
uint64_t getTypeABIAlignment(Type t) const
Returns the required alignment of the given type in the current scope.
This class provides support for representing a failure result, or a valid value of type T.
Definition: LogicalResult.h:78
This class represents a frozen set of patterns that can be processed by a pattern applicator.
Canonicalizes GEPs of which the base type and the pointer's type hint do not match.
LogicalResult matchAndRewrite(GEPOp gep, PatternRewriter &rewriter) const override
Class used for building a 'llvm.getelementptr'.
Definition: LLVMDialect.h:74
Class used for convenient access and iteration over GEP indices.
Definition: LLVMDialect.h:114
iterator begin() const
Returns the begin iterator, iterating over all GEP indices.
Definition: LLVMDialect.h:191
std::conditional_t< std::is_base_of< Attribute, llvm::detail::ValueOfRange< DynamicRange > >::value, Attribute, PointerUnion< IntegerAttr, llvm::detail::ValueOfRange< DynamicRange > >> value_type
Return type of 'operator[]' and the iterators 'operator*'.
Definition: LLVMDialect.h:125
iterator end() const
Returns the end iterator, iterating over all GEP indices.
Definition: LLVMDialect.h:197
LLVM dialect structure type representing a collection of different-typed elements manipulated togethe...
Definition: LLVMTypes.h:109
bool isPacked() const
Checks if a struct is packed.
Definition: LLVMTypes.cpp:482
ArrayRef< Type > getBody() const
Returns the list of element types contained in a non-opaque struct.
Definition: LLVMTypes.cpp:490
Splits GEPs with more than two indices into multiple GEPs with exactly two indices.
Splits stores which write into multiple adjacent elements of an aggregate through a pointer.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition: Location.h:63
Operation * create(const OperationState &state)
Creates an operation given the fields represented as an OperationState.
Definition: Builders.cpp:464
A special type of RewriterBase that coordinates the application of a rewrite pattern on the current I...
Definition: PatternMatch.h:785
MLIRContext * getContext() const
Return the MLIRContext used to create this pattern.
Definition: PatternMatch.h:134
This class coordinates the application of a rewrite on a set of IR, providing a way for clients to tr...
Definition: PatternMatch.h:400
virtual void eraseOp(Operation *op)
This method erases an operation that is known to have no uses.
OpTy replaceOpWithNewOp(Operation *op, Args &&...args)
Replace the results of the given (original) op with a new op that is created without verification (re...
Definition: PatternMatch.h:536
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
Type getType() const
Return the type of this value.
Definition: Value.h:129
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition: Value.cpp:20
std::unique_ptr< Pass > createTypeConsistencyPass()
Creates a pass that adjusts operations operating on pointers so they interpret pointee types as consi...
Include the generated interface declarations.
LogicalResult failure(bool isFailure=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:62
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition: Value.h:498
bool succeeded(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a success value.
Definition: LogicalResult.h:68
LogicalResult success(bool isSuccess=true)
Utility function to generate a LogicalResult.
Definition: LogicalResult.h:56
LogicalResult applyPatternsAndFoldGreedily(Region &region, const FrozenRewritePatternSet &patterns, GreedyRewriteConfig config=GreedyRewriteConfig(), bool *changed=nullptr)
Rewrite ops in the given region, which must be isolated from above, by repeatedly applying the highes...
auto get(MLIRContext *context, Ts &&...params)
Helper method that injects context only if needed, this helps unify some of the attribute constructio...
bool failed(LogicalResult result)
Utility function that returns true if the provided LogicalResult corresponds to a failure value.
Definition: LogicalResult.h:72
This class represents an efficient way to signal success or failure.
Definition: LogicalResult.h:26