MLIR 22.0.0git
LLVMMemorySlot.cpp
Go to the documentation of this file.
1//===- LLVMMemorySlot.cpp - MemorySlot interfaces ---------------*- C++ -*-===//
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//
9// This file implements MemorySlot-related interfaces for LLVM dialect
10// operations.
11//
12//===----------------------------------------------------------------------===//
13
16#include "mlir/IR/Matchers.h"
20#include "llvm/ADT/STLExtras.h"
21#include "llvm/ADT/TypeSwitch.h"
22#include "llvm/Support/DebugLog.h"
23
24#define DEBUG_TYPE "sroa"
25
26using namespace mlir;
27
28//===----------------------------------------------------------------------===//
29// Interfaces for AllocaOp
30//===----------------------------------------------------------------------===//
31
32llvm::SmallVector<MemorySlot> LLVM::AllocaOp::getPromotableSlots() {
33 if (!getOperation()->getBlock()->isEntryBlock())
34 return {};
35
36 return {MemorySlot{getResult(), getElemType()}};
37}
38
39Value LLVM::AllocaOp::getDefaultValue(const MemorySlot &slot,
40 OpBuilder &builder) {
41 return LLVM::UndefOp::create(builder, getLoc(), slot.elemType);
42}
43
44void LLVM::AllocaOp::handleBlockArgument(const MemorySlot &slot,
45 BlockArgument argument,
46 OpBuilder &builder) {
47 for (Operation *user : getOperation()->getUsers())
48 if (auto declareOp = llvm::dyn_cast<LLVM::DbgDeclareOp>(user))
49 LLVM::DbgValueOp::create(builder, declareOp.getLoc(), argument,
50 declareOp.getVarInfo(),
51 declareOp.getLocationExpr());
52}
53
54std::optional<PromotableAllocationOpInterface>
55LLVM::AllocaOp::handlePromotionComplete(const MemorySlot &slot,
56 Value defaultValue,
57 OpBuilder &builder) {
58 if (defaultValue && defaultValue.use_empty())
59 defaultValue.getDefiningOp()->erase();
60 this->erase();
61 return std::nullopt;
62}
63
64SmallVector<DestructurableMemorySlot> LLVM::AllocaOp::getDestructurableSlots() {
65 if (!mlir::matchPattern(getArraySize(), m_One()))
66 return {};
67
68 auto destructurable = dyn_cast<DestructurableTypeInterface>(getElemType());
69 if (!destructurable)
70 return {};
71
72 std::optional<DenseMap<Attribute, Type>> destructuredType =
73 destructurable.getSubelementIndexMap();
74 if (!destructuredType)
75 return {};
76
77 return {DestructurableMemorySlot{{getResult(), getElemType()},
78 *destructuredType}};
79}
80
81DenseMap<Attribute, MemorySlot> LLVM::AllocaOp::destructure(
82 const DestructurableMemorySlot &slot,
83 const SmallPtrSetImpl<Attribute> &usedIndices, OpBuilder &builder,
85 assert(slot.ptr == getResult());
86 builder.setInsertionPointAfter(*this);
87
88 auto destructurableType = cast<DestructurableTypeInterface>(getElemType());
90 for (Attribute index : usedIndices) {
91 Type elemType = destructurableType.getTypeAtIndex(index);
92 assert(elemType && "used index must exist");
93 auto subAlloca = LLVM::AllocaOp::create(
94 builder, getLoc(), LLVM::LLVMPointerType::get(getContext()), elemType,
95 getArraySize());
96 newAllocators.push_back(subAlloca);
97 slotMap.try_emplace<MemorySlot>(index, {subAlloca.getResult(), elemType});
98 }
99
100 return slotMap;
101}
102
103std::optional<DestructurableAllocationOpInterface>
104LLVM::AllocaOp::handleDestructuringComplete(
105 const DestructurableMemorySlot &slot, OpBuilder &builder) {
106 assert(slot.ptr == getResult());
107 this->erase();
108 return std::nullopt;
109}
110
111//===----------------------------------------------------------------------===//
112// Interfaces for LoadOp/StoreOp
113//===----------------------------------------------------------------------===//
114
115bool LLVM::LoadOp::loadsFrom(const MemorySlot &slot) {
116 return getAddr() == slot.ptr;
117}
118
119bool LLVM::LoadOp::storesTo(const MemorySlot &slot) { return false; }
120
121Value LLVM::LoadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
122 Value reachingDef, const DataLayout &dataLayout) {
123 llvm_unreachable("getStored should not be called on LoadOp");
124}
125
126bool LLVM::StoreOp::loadsFrom(const MemorySlot &slot) { return false; }
127
128bool LLVM::StoreOp::storesTo(const MemorySlot &slot) {
129 return getAddr() == slot.ptr;
130}
131
132/// Checks if `type` can be used in any kind of conversion sequences.
134 // Aggregate types are not bitcastable.
135 if (isa<LLVM::LLVMStructType, LLVM::LLVMArrayType>(type))
136 return false;
137
138 if (auto vectorType = dyn_cast<VectorType>(type)) {
139 // Vectors of pointers cannot be casted.
140 if (isa<LLVM::LLVMPointerType>(vectorType.getElementType()))
141 return false;
142 // Scalable types are not supported.
143 return !vectorType.isScalable();
144 }
145 return true;
146}
147
148/// Checks that `rhs` can be converted to `lhs` by a sequence of casts and
149/// truncations. Checks for narrowing or widening conversion compatibility
150/// depending on `narrowingConversion`.
151static bool areConversionCompatible(const DataLayout &layout, Type targetType,
152 Type srcType, bool narrowingConversion) {
153 if (targetType == srcType)
154 return true;
155
156 if (!isSupportedTypeForConversion(targetType) ||
158 return false;
159
160 uint64_t targetSize = layout.getTypeSize(targetType);
161 uint64_t srcSize = layout.getTypeSize(srcType);
162
163 // Pointer casts will only be sane when the bitsize of both pointer types is
164 // the same.
165 if (isa<LLVM::LLVMPointerType>(targetType) &&
166 isa<LLVM::LLVMPointerType>(srcType))
167 return targetSize == srcSize;
168
169 if (narrowingConversion)
170 return targetSize <= srcSize;
171 return targetSize >= srcSize;
172}
173
174/// Checks if `dataLayout` describes a little endian layout.
175static bool isBigEndian(const DataLayout &dataLayout) {
176 auto endiannessStr = dyn_cast_or_null<StringAttr>(dataLayout.getEndianness());
177 return endiannessStr && endiannessStr == "big";
178}
179
180/// Converts a value to an integer type of the same size.
181/// Assumes that the type can be converted.
183 const DataLayout &dataLayout) {
184 Type type = val.getType();
185 assert(isSupportedTypeForConversion(type) &&
186 "expected value to have a convertible type");
187
188 if (isa<IntegerType>(type))
189 return val;
190
191 uint64_t typeBitSize = dataLayout.getTypeSizeInBits(type);
192 IntegerType valueSizeInteger = builder.getIntegerType(typeBitSize);
193
194 if (isa<LLVM::LLVMPointerType>(type))
195 return builder.createOrFold<LLVM::PtrToIntOp>(loc, valueSizeInteger, val);
196 return builder.createOrFold<LLVM::BitcastOp>(loc, valueSizeInteger, val);
197}
198
199/// Converts a value with an integer type to `targetType`.
201 Value val, Type targetType) {
202 assert(isa<IntegerType>(val.getType()) &&
203 "expected value to have an integer type");
204 assert(isSupportedTypeForConversion(targetType) &&
205 "expected the target type to be supported for conversions");
206 if (val.getType() == targetType)
207 return val;
208 if (isa<LLVM::LLVMPointerType>(targetType))
209 return builder.createOrFold<LLVM::IntToPtrOp>(loc, targetType, val);
210 return builder.createOrFold<LLVM::BitcastOp>(loc, targetType, val);
211}
212
213/// Constructs operations that convert `srcValue` into a new value of type
214/// `targetType`. Assumes the types have the same bitsize.
216 Value srcValue, Type targetType,
217 const DataLayout &dataLayout) {
218 Type srcType = srcValue.getType();
219 assert(areConversionCompatible(dataLayout, targetType, srcType,
220 /*narrowingConversion=*/true) &&
221 "expected that the compatibility was checked before");
222
223 // Nothing has to be done if the types are already the same.
224 if (srcType == targetType)
225 return srcValue;
226
227 // In the special case of casting one pointer to another, we want to generate
228 // an address space cast. Bitcasts of pointers are not allowed and using
229 // pointer to integer conversions are not equivalent due to the loss of
230 // provenance.
231 if (isa<LLVM::LLVMPointerType>(targetType) &&
232 isa<LLVM::LLVMPointerType>(srcType))
233 return builder.createOrFold<LLVM::AddrSpaceCastOp>(loc, targetType,
234 srcValue);
235
236 // For all other castable types, casting through integers is necessary.
237 Value replacement = castToSameSizedInt(builder, loc, srcValue, dataLayout);
238 return castIntValueToSameSizedType(builder, loc, replacement, targetType);
239}
240
241/// Constructs operations that convert `srcValue` into a new value of type
242/// `targetType`. Performs bit-level extraction if the source type is larger
243/// than the target type. Assumes that this conversion is possible.
245 Value srcValue, Type targetType,
246 const DataLayout &dataLayout) {
247 // Get the types of the source and target values.
248 Type srcType = srcValue.getType();
249 assert(areConversionCompatible(dataLayout, targetType, srcType,
250 /*narrowingConversion=*/true) &&
251 "expected that the compatibility was checked before");
252
253 uint64_t srcTypeSize = dataLayout.getTypeSizeInBits(srcType);
254 uint64_t targetTypeSize = dataLayout.getTypeSizeInBits(targetType);
255 if (srcTypeSize == targetTypeSize)
256 return castSameSizedTypes(builder, loc, srcValue, targetType, dataLayout);
257
258 // First, cast the value to a same-sized integer type.
259 Value replacement = castToSameSizedInt(builder, loc, srcValue, dataLayout);
260
261 // Truncate the integer if the size of the target is less than the value.
262 if (isBigEndian(dataLayout)) {
263 uint64_t shiftAmount = srcTypeSize - targetTypeSize;
264 auto shiftConstant = LLVM::ConstantOp::create(
265 builder, loc, builder.getIntegerAttr(srcType, shiftAmount));
267 builder.createOrFold<LLVM::LShrOp>(loc, srcValue, shiftConstant);
268 }
269
270 replacement = LLVM::TruncOp::create(
271 builder, loc, builder.getIntegerType(targetTypeSize), replacement);
272
273 // Now cast the integer to the actual target type if required.
274 return castIntValueToSameSizedType(builder, loc, replacement, targetType);
275}
276
277/// Constructs operations that insert the bits of `srcValue` into the
278/// "beginning" of `reachingDef` (beginning is endianness dependent).
279/// Assumes that this conversion is possible.
281 Value srcValue, Value reachingDef,
282 const DataLayout &dataLayout) {
283
284 assert(areConversionCompatible(dataLayout, reachingDef.getType(),
285 srcValue.getType(),
286 /*narrowingConversion=*/false) &&
287 "expected that the compatibility was checked before");
288 uint64_t valueTypeSize = dataLayout.getTypeSizeInBits(srcValue.getType());
289 uint64_t slotTypeSize = dataLayout.getTypeSizeInBits(reachingDef.getType());
290 if (slotTypeSize == valueTypeSize)
291 return castSameSizedTypes(builder, loc, srcValue, reachingDef.getType(),
292 dataLayout);
293
294 // In the case where the store only overwrites parts of the memory,
295 // bit fiddling is required to construct the new value.
296
297 // First convert both values to integers of the same size.
298 Value defAsInt = castToSameSizedInt(builder, loc, reachingDef, dataLayout);
299 Value valueAsInt = castToSameSizedInt(builder, loc, srcValue, dataLayout);
300 // Extend the value to the size of the reaching definition.
301 valueAsInt =
302 builder.createOrFold<LLVM::ZExtOp>(loc, defAsInt.getType(), valueAsInt);
303 uint64_t sizeDifference = slotTypeSize - valueTypeSize;
304 if (isBigEndian(dataLayout)) {
305 // On big endian systems, a store to the base pointer overwrites the most
306 // significant bits. To accomodate for this, the stored value needs to be
307 // shifted into the according position.
308 Value bigEndianShift = LLVM::ConstantOp::create(
309 builder, loc,
310 builder.getIntegerAttr(defAsInt.getType(), sizeDifference));
311 valueAsInt =
312 builder.createOrFold<LLVM::ShlOp>(loc, valueAsInt, bigEndianShift);
313 }
314
315 // Construct the mask that is used to erase the bits that are overwritten by
316 // the store.
317 APInt maskValue;
318 if (isBigEndian(dataLayout)) {
319 // Build a mask that has the most significant bits set to zero.
320 // Note: This is the same as 2^sizeDifference - 1
321 maskValue = APInt::getAllOnes(sizeDifference).zext(slotTypeSize);
322 } else {
323 // Build a mask that has the least significant bits set to zero.
324 // Note: This is the same as -(2^valueTypeSize)
325 maskValue = APInt::getAllOnes(valueTypeSize).zext(slotTypeSize);
326 maskValue.flipAllBits();
327 }
328
329 // Mask out the affected bits ...
330 Value mask = LLVM::ConstantOp::create(
331 builder, loc, builder.getIntegerAttr(defAsInt.getType(), maskValue));
332 Value masked = builder.createOrFold<LLVM::AndOp>(loc, defAsInt, mask);
333
334 // ... and combine the result with the new value.
335 Value combined = builder.createOrFold<LLVM::OrOp>(loc, masked, valueAsInt);
336
337 return castIntValueToSameSizedType(builder, loc, combined,
338 reachingDef.getType());
339}
340
341Value LLVM::StoreOp::getStored(const MemorySlot &slot, OpBuilder &builder,
342 Value reachingDef,
343 const DataLayout &dataLayout) {
344 assert(reachingDef && reachingDef.getType() == slot.elemType &&
345 "expected the reaching definition's type to match the slot's type");
346 return createInsertAndCast(builder, getLoc(), getValue(), reachingDef,
347 dataLayout);
348}
349
350bool LLVM::LoadOp::canUsesBeRemoved(
351 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
352 SmallVectorImpl<OpOperand *> &newBlockingUses,
353 const DataLayout &dataLayout) {
354 if (blockingUses.size() != 1)
355 return false;
356 Value blockingUse = (*blockingUses.begin())->get();
357 // If the blocking use is the slot ptr itself, there will be enough
358 // context to reconstruct the result of the load at removal time, so it can
359 // be removed (provided it is not volatile).
360 return blockingUse == slot.ptr && getAddr() == slot.ptr &&
361 areConversionCompatible(dataLayout, getResult().getType(),
362 slot.elemType, /*narrowingConversion=*/true) &&
363 !getVolatile_();
364}
365
366DeletionKind LLVM::LoadOp::removeBlockingUses(
367 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
368 OpBuilder &builder, Value reachingDefinition,
369 const DataLayout &dataLayout) {
370 // `canUsesBeRemoved` checked this blocking use must be the loaded slot
371 // pointer.
372 Value newResult = createExtractAndCast(builder, getLoc(), reachingDefinition,
373 getResult().getType(), dataLayout);
374 getResult().replaceAllUsesWith(newResult);
376}
377
378bool LLVM::StoreOp::canUsesBeRemoved(
379 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
380 SmallVectorImpl<OpOperand *> &newBlockingUses,
381 const DataLayout &dataLayout) {
382 if (blockingUses.size() != 1)
383 return false;
384 Value blockingUse = (*blockingUses.begin())->get();
385 // If the blocking use is the slot ptr itself, dropping the store is
386 // fine, provided we are currently promoting its target value. Don't allow a
387 // store OF the slot pointer, only INTO the slot pointer.
388 return blockingUse == slot.ptr && getAddr() == slot.ptr &&
389 getValue() != slot.ptr &&
390 areConversionCompatible(dataLayout, slot.elemType,
391 getValue().getType(),
392 /*narrowingConversion=*/false) &&
393 !getVolatile_();
394}
395
396DeletionKind LLVM::StoreOp::removeBlockingUses(
397 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
398 OpBuilder &builder, Value reachingDefinition,
399 const DataLayout &dataLayout) {
401}
402
403/// Checks if `slot` can be accessed through the provided access type.
404static bool isValidAccessType(const MemorySlot &slot, Type accessType,
405 const DataLayout &dataLayout) {
406 return dataLayout.getTypeSize(accessType) <=
407 dataLayout.getTypeSize(slot.elemType);
408}
409
410LogicalResult LLVM::LoadOp::ensureOnlySafeAccesses(
411 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
412 const DataLayout &dataLayout) {
413 return success(getAddr() != slot.ptr ||
414 isValidAccessType(slot, getType(), dataLayout));
415}
416
417LogicalResult LLVM::StoreOp::ensureOnlySafeAccesses(
418 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
419 const DataLayout &dataLayout) {
420 return success(getAddr() != slot.ptr ||
421 isValidAccessType(slot, getValue().getType(), dataLayout));
422}
423
424/// Returns the subslot's type at the requested index.
427 auto subelementIndexMap =
428 cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap();
429 if (!subelementIndexMap)
430 return {};
431 assert(!subelementIndexMap->empty());
432
433 // Note: Returns a null-type when no entry was found.
434 return subelementIndexMap->lookup(index);
435}
436
437bool LLVM::LoadOp::canRewire(const DestructurableMemorySlot &slot,
438 SmallPtrSetImpl<Attribute> &usedIndices,
439 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
440 const DataLayout &dataLayout) {
441 if (getVolatile_())
442 return false;
443
444 // A load always accesses the first element of the destructured slot.
445 auto index = IntegerAttr::get(IntegerType::get(getContext(), 32), 0);
446 Type subslotType = getTypeAtIndex(slot, index);
447 if (!subslotType)
448 return false;
449
450 // The access can only be replaced when the subslot is read within its bounds.
451 if (dataLayout.getTypeSize(getType()) > dataLayout.getTypeSize(subslotType))
452 return false;
453
454 usedIndices.insert(index);
455 return true;
456}
457
458DeletionKind LLVM::LoadOp::rewire(const DestructurableMemorySlot &slot,
460 OpBuilder &builder,
461 const DataLayout &dataLayout) {
462 auto index = IntegerAttr::get(IntegerType::get(getContext(), 32), 0);
463 auto it = subslots.find(index);
464 assert(it != subslots.end());
465
466 getAddrMutable().set(it->getSecond().ptr);
467 return DeletionKind::Keep;
468}
469
470bool LLVM::StoreOp::canRewire(const DestructurableMemorySlot &slot,
471 SmallPtrSetImpl<Attribute> &usedIndices,
472 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
473 const DataLayout &dataLayout) {
474 if (getVolatile_())
475 return false;
476
477 // Storing the pointer to memory cannot be dealt with.
478 if (getValue() == slot.ptr)
479 return false;
480
481 // A store always accesses the first element of the destructured slot.
482 auto index = IntegerAttr::get(IntegerType::get(getContext(), 32), 0);
483 Type subslotType = getTypeAtIndex(slot, index);
484 if (!subslotType)
485 return false;
486
487 // The access can only be replaced when the subslot is read within its bounds.
488 if (dataLayout.getTypeSize(getValue().getType()) >
489 dataLayout.getTypeSize(subslotType))
490 return false;
491
492 usedIndices.insert(index);
493 return true;
494}
495
496DeletionKind LLVM::StoreOp::rewire(const DestructurableMemorySlot &slot,
498 OpBuilder &builder,
499 const DataLayout &dataLayout) {
500 auto index = IntegerAttr::get(IntegerType::get(getContext(), 32), 0);
501 auto it = subslots.find(index);
502 assert(it != subslots.end());
503
504 getAddrMutable().set(it->getSecond().ptr);
505 return DeletionKind::Keep;
506}
507
508//===----------------------------------------------------------------------===//
509// Interfaces for discardable OPs
510//===----------------------------------------------------------------------===//
511
512/// Conditions the deletion of the operation to the removal of all its uses.
513static bool forwardToUsers(Operation *op,
514 SmallVectorImpl<OpOperand *> &newBlockingUses) {
515 for (Value result : op->getResults())
516 for (OpOperand &use : result.getUses())
517 newBlockingUses.push_back(&use);
518 return true;
519}
520
521bool LLVM::BitcastOp::canUsesBeRemoved(
522 const SmallPtrSetImpl<OpOperand *> &blockingUses,
523 SmallVectorImpl<OpOperand *> &newBlockingUses,
524 const DataLayout &dataLayout) {
525 return forwardToUsers(*this, newBlockingUses);
526}
527
528DeletionKind LLVM::BitcastOp::removeBlockingUses(
529 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
531}
532
533bool LLVM::AddrSpaceCastOp::canUsesBeRemoved(
534 const SmallPtrSetImpl<OpOperand *> &blockingUses,
535 SmallVectorImpl<OpOperand *> &newBlockingUses,
536 const DataLayout &dataLayout) {
537 return forwardToUsers(*this, newBlockingUses);
538}
539
540DeletionKind LLVM::AddrSpaceCastOp::removeBlockingUses(
541 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
543}
544
545bool LLVM::LifetimeStartOp::canUsesBeRemoved(
546 const SmallPtrSetImpl<OpOperand *> &blockingUses,
547 SmallVectorImpl<OpOperand *> &newBlockingUses,
548 const DataLayout &dataLayout) {
549 return true;
550}
551
552DeletionKind LLVM::LifetimeStartOp::removeBlockingUses(
553 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
555}
556
557bool LLVM::LifetimeEndOp::canUsesBeRemoved(
558 const SmallPtrSetImpl<OpOperand *> &blockingUses,
559 SmallVectorImpl<OpOperand *> &newBlockingUses,
560 const DataLayout &dataLayout) {
561 return true;
562}
563
564DeletionKind LLVM::LifetimeEndOp::removeBlockingUses(
565 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
567}
568
569bool LLVM::InvariantStartOp::canUsesBeRemoved(
570 const SmallPtrSetImpl<OpOperand *> &blockingUses,
571 SmallVectorImpl<OpOperand *> &newBlockingUses,
572 const DataLayout &dataLayout) {
573 return true;
574}
575
576DeletionKind LLVM::InvariantStartOp::removeBlockingUses(
577 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
579}
580
581bool LLVM::InvariantEndOp::canUsesBeRemoved(
582 const SmallPtrSetImpl<OpOperand *> &blockingUses,
583 SmallVectorImpl<OpOperand *> &newBlockingUses,
584 const DataLayout &dataLayout) {
585 return true;
586}
587
588DeletionKind LLVM::InvariantEndOp::removeBlockingUses(
589 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
591}
592
593bool LLVM::LaunderInvariantGroupOp::canUsesBeRemoved(
594 const SmallPtrSetImpl<OpOperand *> &blockingUses,
595 SmallVectorImpl<OpOperand *> &newBlockingUses,
596 const DataLayout &dataLayout) {
597 return forwardToUsers(*this, newBlockingUses);
598}
599
600DeletionKind LLVM::LaunderInvariantGroupOp::removeBlockingUses(
601 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
603}
604
605bool LLVM::StripInvariantGroupOp::canUsesBeRemoved(
606 const SmallPtrSetImpl<OpOperand *> &blockingUses,
607 SmallVectorImpl<OpOperand *> &newBlockingUses,
608 const DataLayout &dataLayout) {
609 return forwardToUsers(*this, newBlockingUses);
610}
611
612DeletionKind LLVM::StripInvariantGroupOp::removeBlockingUses(
613 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
615}
616
617bool LLVM::DbgDeclareOp::canUsesBeRemoved(
618 const SmallPtrSetImpl<OpOperand *> &blockingUses,
619 SmallVectorImpl<OpOperand *> &newBlockingUses,
620 const DataLayout &dataLayout) {
621 return true;
622}
623
624DeletionKind LLVM::DbgDeclareOp::removeBlockingUses(
625 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
627}
628
629bool LLVM::DbgValueOp::canUsesBeRemoved(
630 const SmallPtrSetImpl<OpOperand *> &blockingUses,
631 SmallVectorImpl<OpOperand *> &newBlockingUses,
632 const DataLayout &dataLayout) {
633 // There is only one operand that we can remove the use of.
634 if (blockingUses.size() != 1)
635 return false;
636
637 return (*blockingUses.begin())->get() == getValue();
638}
639
640DeletionKind LLVM::DbgValueOp::removeBlockingUses(
641 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
642 // builder by default is after '*this', but we need it before '*this'.
643 builder.setInsertionPoint(*this);
644
645 // Rather than dropping the debug value, replace it with undef to preserve the
646 // debug local variable info. This allows the debugger to inform the user that
647 // the variable has been optimized out.
648 auto undef =
649 UndefOp::create(builder, getValue().getLoc(), getValue().getType());
650 getValueMutable().assign(undef);
651 return DeletionKind::Keep;
652}
653
654bool LLVM::DbgDeclareOp::requiresReplacedValues() { return true; }
655
656void LLVM::DbgDeclareOp::visitReplacedValues(
657 ArrayRef<std::pair<Operation *, Value>> definitions, OpBuilder &builder) {
658 for (auto [op, value] : definitions) {
659 builder.setInsertionPointAfter(op);
660 LLVM::DbgValueOp::create(builder, getLoc(), value, getVarInfo(),
661 getLocationExpr());
662 }
663}
664
665//===----------------------------------------------------------------------===//
666// Interfaces for GEPOp
667//===----------------------------------------------------------------------===//
668
669static bool hasAllZeroIndices(LLVM::GEPOp gepOp) {
670 return llvm::all_of(gepOp.getIndices(), [](auto index) {
671 auto indexAttr = llvm::dyn_cast_if_present<IntegerAttr>(index);
672 return indexAttr && indexAttr.getValue() == 0;
673 });
674}
675
676bool LLVM::GEPOp::canUsesBeRemoved(
677 const SmallPtrSetImpl<OpOperand *> &blockingUses,
678 SmallVectorImpl<OpOperand *> &newBlockingUses,
679 const DataLayout &dataLayout) {
680 // GEP can be removed as long as it is a no-op and its users can be removed.
681 if (!hasAllZeroIndices(*this))
682 return false;
683 return forwardToUsers(*this, newBlockingUses);
684}
685
686DeletionKind LLVM::GEPOp::removeBlockingUses(
687 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
689}
690
691/// Returns the amount of bytes the provided GEP elements will offset the
692/// pointer by. Returns nullopt if no constant offset could be computed.
693static std::optional<uint64_t> gepToByteOffset(const DataLayout &dataLayout,
694 LLVM::GEPOp gep) {
695 // Collects all indices.
697 for (auto index : gep.getIndices()) {
698 auto constIndex = dyn_cast<IntegerAttr>(index);
699 if (!constIndex)
700 return {};
701 int64_t gepIndex = constIndex.getInt();
702 // Negative indices are not supported.
703 if (gepIndex < 0)
704 return {};
705 indices.push_back(gepIndex);
706 }
707
708 Type currentType = gep.getElemType();
709 uint64_t offset = indices[0] * dataLayout.getTypeSize(currentType);
710
711 for (uint64_t index : llvm::drop_begin(indices)) {
712 bool shouldCancel =
713 TypeSwitch<Type, bool>(currentType)
714 .Case([&](LLVM::LLVMArrayType arrayType) {
715 offset +=
716 index * dataLayout.getTypeSize(arrayType.getElementType());
717 currentType = arrayType.getElementType();
718 return false;
719 })
720 .Case([&](LLVM::LLVMStructType structType) {
721 ArrayRef<Type> body = structType.getBody();
722 assert(index < body.size() && "expected valid struct indexing");
723 for (uint32_t i : llvm::seq(index)) {
724 if (!structType.isPacked())
725 offset = llvm::alignTo(
726 offset, dataLayout.getTypeABIAlignment(body[i]));
727 offset += dataLayout.getTypeSize(body[i]);
728 }
729
730 // Align for the current type as well.
731 if (!structType.isPacked())
732 offset = llvm::alignTo(
733 offset, dataLayout.getTypeABIAlignment(body[index]));
734 currentType = body[index];
735 return false;
736 })
737 .Default([&](Type type) {
738 LDBG() << "[sroa] Unsupported type for offset computations"
739 << type;
740 return true;
741 });
742
743 if (shouldCancel)
744 return std::nullopt;
745 }
746
747 return offset;
748}
749
750namespace {
751/// A struct that stores both the index into the aggregate type of the slot as
752/// well as the corresponding byte offset in memory.
753struct SubslotAccessInfo {
754 /// The parent slot's index that the access falls into.
755 uint32_t index;
756 /// The offset into the subslot of the access.
757 uint64_t subslotOffset;
758};
759} // namespace
760
761/// Computes subslot access information for an access into `slot` with the given
762/// offset.
763/// Returns nullopt when the offset is out-of-bounds or when the access is into
764/// the padding of `slot`.
765static std::optional<SubslotAccessInfo>
767 const DataLayout &dataLayout, LLVM::GEPOp gep) {
768 std::optional<uint64_t> offset = gepToByteOffset(dataLayout, gep);
769 if (!offset)
770 return {};
771
772 // Helper to check that a constant index is in the bounds of the GEP index
773 // representation. LLVM dialects's GEP arguments have a limited bitwidth, thus
774 // this additional check is necessary.
775 auto isOutOfBoundsGEPIndex = [](uint64_t index) {
776 return index >= (1 << LLVM::kGEPConstantBitWidth);
777 };
778
779 Type type = slot.elemType;
780 if (*offset >= dataLayout.getTypeSize(type))
781 return {};
783 .Case([&](LLVM::LLVMArrayType arrayType)
784 -> std::optional<SubslotAccessInfo> {
785 // Find which element of the array contains the offset.
786 uint64_t elemSize = dataLayout.getTypeSize(arrayType.getElementType());
787 uint64_t index = *offset / elemSize;
788 if (isOutOfBoundsGEPIndex(index))
789 return {};
790 return SubslotAccessInfo{static_cast<uint32_t>(index),
791 *offset - (index * elemSize)};
792 })
793 .Case([&](LLVM::LLVMStructType structType)
794 -> std::optional<SubslotAccessInfo> {
795 uint64_t distanceToStart = 0;
796 // Walk over the elements of the struct to find in which of
797 // them the offset is.
798 for (auto [index, elem] : llvm::enumerate(structType.getBody())) {
799 uint64_t elemSize = dataLayout.getTypeSize(elem);
800 if (!structType.isPacked()) {
801 distanceToStart = llvm::alignTo(
802 distanceToStart, dataLayout.getTypeABIAlignment(elem));
803 // If the offset is in padding, cancel the rewrite.
804 if (offset < distanceToStart)
805 return {};
806 }
807
808 if (offset < distanceToStart + elemSize) {
809 if (isOutOfBoundsGEPIndex(index))
810 return {};
811 // The offset is within this element, stop iterating the
812 // struct and return the index.
813 return SubslotAccessInfo{static_cast<uint32_t>(index),
814 *offset - distanceToStart};
815 }
816
817 // The offset is not within this element, continue walking
818 // over the struct.
819 distanceToStart += elemSize;
820 }
821
822 return {};
823 });
824}
825
826/// Constructs a byte array type of the given size.
827static LLVM::LLVMArrayType getByteArrayType(MLIRContext *context,
828 unsigned size) {
829 auto byteType = IntegerType::get(context, 8);
830 return LLVM::LLVMArrayType::get(context, byteType, size);
831}
832
833LogicalResult LLVM::GEPOp::ensureOnlySafeAccesses(
834 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
835 const DataLayout &dataLayout) {
836 if (getBase() != slot.ptr)
837 return success();
838 std::optional<uint64_t> gepOffset = gepToByteOffset(dataLayout, *this);
839 if (!gepOffset)
840 return failure();
841 uint64_t slotSize = dataLayout.getTypeSize(slot.elemType);
842 // Check that the access is strictly inside the slot.
843 if (*gepOffset >= slotSize)
844 return failure();
845 // Every access that remains in bounds of the remaining slot is considered
846 // legal.
847 mustBeSafelyUsed.emplace_back<MemorySlot>(
848 {getRes(), getByteArrayType(getContext(), slotSize - *gepOffset)});
849 return success();
850}
851
852bool LLVM::GEPOp::canRewire(const DestructurableMemorySlot &slot,
853 SmallPtrSetImpl<Attribute> &usedIndices,
854 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
855 const DataLayout &dataLayout) {
856 if (!isa<LLVM::LLVMPointerType>(getBase().getType()))
857 return false;
858
859 if (getBase() != slot.ptr)
860 return false;
861 std::optional<SubslotAccessInfo> accessInfo =
862 getSubslotAccessInfo(slot, dataLayout, *this);
863 if (!accessInfo)
864 return false;
865 auto indexAttr =
866 IntegerAttr::get(IntegerType::get(getContext(), 32), accessInfo->index);
867 assert(slot.subelementTypes.contains(indexAttr));
868 usedIndices.insert(indexAttr);
869
870 // The remainder of the subslot should be accesses in-bounds. Thus, we create
871 // a dummy slot with the size of the remainder.
872 Type subslotType = slot.subelementTypes.lookup(indexAttr);
873 uint64_t slotSize = dataLayout.getTypeSize(subslotType);
874 LLVM::LLVMArrayType remainingSlotType =
875 getByteArrayType(getContext(), slotSize - accessInfo->subslotOffset);
876 mustBeSafelyUsed.emplace_back<MemorySlot>({getRes(), remainingSlotType});
877
878 return true;
879}
880
881DeletionKind LLVM::GEPOp::rewire(const DestructurableMemorySlot &slot,
883 OpBuilder &builder,
884 const DataLayout &dataLayout) {
885 std::optional<SubslotAccessInfo> accessInfo =
886 getSubslotAccessInfo(slot, dataLayout, *this);
887 assert(accessInfo && "expected access info to be checked before");
888 auto indexAttr =
889 IntegerAttr::get(IntegerType::get(getContext(), 32), accessInfo->index);
890 const MemorySlot &newSlot = subslots.at(indexAttr);
891
892 auto byteType = IntegerType::get(builder.getContext(), 8);
893 auto newPtr = builder.createOrFold<LLVM::GEPOp>(
894 getLoc(), getResult().getType(), byteType, newSlot.ptr,
895 ArrayRef<GEPArg>(accessInfo->subslotOffset), getNoWrapFlags());
896 getResult().replaceAllUsesWith(newPtr);
898}
899
900//===----------------------------------------------------------------------===//
901// Utilities for memory intrinsics
902//===----------------------------------------------------------------------===//
903
904namespace {
905
906/// Returns the length of the given memory intrinsic in bytes if it can be known
907/// at compile-time on a best-effort basis, nothing otherwise.
908template <class MemIntr>
909std::optional<uint64_t> getStaticMemIntrLen(MemIntr op) {
910 APInt memIntrLen;
911 if (!matchPattern(op.getLen(), m_ConstantInt(&memIntrLen)))
912 return {};
913 if (memIntrLen.getBitWidth() > 64)
914 return {};
915 return memIntrLen.getZExtValue();
916}
917
918/// Returns the length of the given memory intrinsic in bytes if it can be known
919/// at compile-time on a best-effort basis, nothing otherwise.
920/// Because MemcpyInlineOp has its length encoded as an attribute, this requires
921/// specialized handling.
922template <>
923std::optional<uint64_t> getStaticMemIntrLen(LLVM::MemcpyInlineOp op) {
924 APInt memIntrLen = op.getLen();
925 if (memIntrLen.getBitWidth() > 64)
926 return {};
927 return memIntrLen.getZExtValue();
928}
929
930/// Returns the length of the given memory intrinsic in bytes if it can be known
931/// at compile-time on a best-effort basis, nothing otherwise.
932/// Because MemsetInlineOp has its length encoded as an attribute, this requires
933/// specialized handling.
934template <>
935std::optional<uint64_t> getStaticMemIntrLen(LLVM::MemsetInlineOp op) {
936 APInt memIntrLen = op.getLen();
937 if (memIntrLen.getBitWidth() > 64)
938 return {};
939 return memIntrLen.getZExtValue();
940}
941
942/// Returns an integer attribute representing the length of a memset intrinsic
943template <class MemsetIntr>
944IntegerAttr createMemsetLenAttr(MemsetIntr op) {
945 IntegerAttr memsetLenAttr;
946 bool successfulMatch =
947 matchPattern(op.getLen(), m_Constant<IntegerAttr>(&memsetLenAttr));
948 (void)successfulMatch;
949 assert(successfulMatch);
950 return memsetLenAttr;
951}
952
953/// Returns an integer attribute representing the length of a memset intrinsic
954/// Because MemsetInlineOp has its length encoded as an attribute, this requires
955/// specialized handling.
956template <>
957IntegerAttr createMemsetLenAttr(LLVM::MemsetInlineOp op) {
958 return op.getLenAttr();
959}
960
961/// Creates a memset intrinsic of that matches the `toReplace` intrinsic
962/// using the provided parameters. There are template specializations for
963/// MemsetOp and MemsetInlineOp.
964template <class MemsetIntr>
965void createMemsetIntr(OpBuilder &builder, MemsetIntr toReplace,
966 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
969
970template <>
971void createMemsetIntr(OpBuilder &builder, LLVM::MemsetOp toReplace,
972 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
975 Value newMemsetSizeValue =
976 LLVM::ConstantOp::create(
977 builder, toReplace.getLen().getLoc(),
978 IntegerAttr::get(memsetLenAttr.getType(), newMemsetSize))
979 .getResult();
980
981 LLVM::MemsetOp::create(builder, toReplace.getLoc(), subslots.at(index).ptr,
982 toReplace.getVal(), newMemsetSizeValue,
983 toReplace.getIsVolatile());
984}
985
986template <>
987void createMemsetIntr(OpBuilder &builder, LLVM::MemsetInlineOp toReplace,
988 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
991 auto newMemsetSizeValue =
992 IntegerAttr::get(memsetLenAttr.getType(), newMemsetSize);
993
994 LLVM::MemsetInlineOp::create(builder, toReplace.getLoc(),
995 subslots.at(index).ptr, toReplace.getVal(),
996 newMemsetSizeValue, toReplace.getIsVolatile());
997}
998
999} // namespace
1000
1001/// Returns whether one can be sure the memory intrinsic does not write outside
1002/// of the bounds of the given slot, on a best-effort basis.
1003template <class MemIntr>
1004static bool definitelyWritesOnlyWithinSlot(MemIntr op, const MemorySlot &slot,
1005 const DataLayout &dataLayout) {
1006 if (!isa<LLVM::LLVMPointerType>(slot.ptr.getType()) ||
1007 op.getDst() != slot.ptr)
1008 return false;
1009
1010 std::optional<uint64_t> memIntrLen = getStaticMemIntrLen(op);
1011 return memIntrLen && *memIntrLen <= dataLayout.getTypeSize(slot.elemType);
1012}
1013
1014/// Checks whether all indices are i32. This is used to check GEPs can index
1015/// into them.
1017 Type i32 = IntegerType::get(slot.ptr.getContext(), 32);
1018 return llvm::all_of(llvm::make_first_range(slot.subelementTypes),
1019 [&](Attribute index) {
1020 auto intIndex = dyn_cast<IntegerAttr>(index);
1021 return intIndex && intIndex.getType() == i32;
1022 });
1023}
1024
1025//===----------------------------------------------------------------------===//
1026// Interfaces for memset and memset.inline
1027//===----------------------------------------------------------------------===//
1028
1029template <class MemsetIntr>
1030static bool memsetCanRewire(MemsetIntr op, const DestructurableMemorySlot &slot,
1031 SmallPtrSetImpl<Attribute> &usedIndices,
1032 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1033 const DataLayout &dataLayout) {
1034 if (&slot.elemType.getDialect() != op.getOperation()->getDialect())
1035 return false;
1036
1037 if (op.getIsVolatile())
1038 return false;
1039
1040 if (!cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap())
1041 return false;
1042
1043 if (!areAllIndicesI32(slot))
1044 return false;
1045
1046 return definitelyWritesOnlyWithinSlot(op, slot, dataLayout);
1047}
1048
1049template <class MemsetIntr>
1050static Value memsetGetStored(MemsetIntr op, const MemorySlot &slot,
1051 OpBuilder &builder) {
1052 /// Returns an integer value that is `width` bits wide representing the value
1053 /// assigned to the slot by memset.
1054 auto buildMemsetValue = [&](unsigned width) -> Value {
1055 assert(width % 8 == 0);
1056 auto intType = IntegerType::get(op.getContext(), width);
1057
1058 // If we know the pattern at compile time, we can compute and assign a
1059 // constant directly.
1060 IntegerAttr constantPattern;
1061 if (matchPattern(op.getVal(), m_Constant(&constantPattern))) {
1062 assert(constantPattern.getValue().getBitWidth() == 8);
1063 APInt memsetVal(/*numBits=*/width, /*val=*/0);
1064 for (unsigned loBit = 0; loBit < width; loBit += 8)
1065 memsetVal.insertBits(constantPattern.getValue(), loBit);
1066 return LLVM::ConstantOp::create(builder, op.getLoc(),
1067 IntegerAttr::get(intType, memsetVal));
1068 }
1069
1070 // If the output is a single byte, we can return the pattern directly.
1071 if (width == 8)
1072 return op.getVal();
1073
1074 // Otherwise build the memset integer at runtime by repeatedly shifting the
1075 // value and or-ing it with the previous value.
1076 uint64_t coveredBits = 8;
1077 Value currentValue =
1078 LLVM::ZExtOp::create(builder, op.getLoc(), intType, op.getVal());
1079 while (coveredBits < width) {
1080 Value shiftBy =
1081 LLVM::ConstantOp::create(builder, op.getLoc(), intType, coveredBits);
1082 Value shifted =
1083 LLVM::ShlOp::create(builder, op.getLoc(), currentValue, shiftBy);
1084 currentValue =
1085 LLVM::OrOp::create(builder, op.getLoc(), currentValue, shifted);
1086 coveredBits *= 2;
1087 }
1088
1089 return currentValue;
1090 };
1092 .Case([&](IntegerType type) -> Value {
1093 return buildMemsetValue(type.getWidth());
1094 })
1095 .Case([&](FloatType type) -> Value {
1096 Value intVal = buildMemsetValue(type.getWidth());
1097 return LLVM::BitcastOp::create(builder, op.getLoc(), type, intVal);
1098 })
1099 .DefaultUnreachable(
1100 "getStored should not be called on memset to unsupported type");
1101}
1102
1103template <class MemsetIntr>
1104static bool
1105memsetCanUsesBeRemoved(MemsetIntr op, const MemorySlot &slot,
1106 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1107 SmallVectorImpl<OpOperand *> &newBlockingUses,
1108 const DataLayout &dataLayout) {
1109 bool canConvertType =
1111 .Case<IntegerType, FloatType>([](auto type) {
1112 return type.getWidth() % 8 == 0 && type.getWidth() > 0;
1113 })
1114 .Default(false);
1115 if (!canConvertType)
1116 return false;
1117
1118 if (op.getIsVolatile())
1119 return false;
1120
1121 return getStaticMemIntrLen(op) == dataLayout.getTypeSize(slot.elemType);
1122}
1123
1124template <class MemsetIntr>
1125static DeletionKind
1126memsetRewire(MemsetIntr op, const DestructurableMemorySlot &slot,
1127 DenseMap<Attribute, MemorySlot> &subslots, OpBuilder &builder,
1128 const DataLayout &dataLayout) {
1129
1130 std::optional<DenseMap<Attribute, Type>> types =
1131 cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap();
1132
1133 IntegerAttr memsetLenAttr = createMemsetLenAttr(op);
1134
1135 bool packed = false;
1136 if (auto structType = dyn_cast<LLVM::LLVMStructType>(slot.elemType))
1137 packed = structType.isPacked();
1138
1139 Type i32 = IntegerType::get(op.getContext(), 32);
1140 uint64_t memsetLen = memsetLenAttr.getValue().getZExtValue();
1141 uint64_t covered = 0;
1142 for (size_t i = 0; i < types->size(); i++) {
1143 // Create indices on the fly to get elements in the right order.
1144 Attribute index = IntegerAttr::get(i32, i);
1145 Type elemType = types->at(index);
1146 uint64_t typeSize = dataLayout.getTypeSize(elemType);
1147
1148 if (!packed)
1149 covered =
1150 llvm::alignTo(covered, dataLayout.getTypeABIAlignment(elemType));
1151
1152 if (covered >= memsetLen)
1153 break;
1154
1155 // If this subslot is used, apply a new memset to it.
1156 // Otherwise, only compute its offset within the original memset.
1157 if (subslots.contains(index)) {
1158 uint64_t newMemsetSize = std::min(memsetLen - covered, typeSize);
1159 createMemsetIntr(builder, op, memsetLenAttr, newMemsetSize, subslots,
1160 index);
1161 }
1162
1163 covered += typeSize;
1164 }
1165
1166 return DeletionKind::Delete;
1167}
1168
1169bool LLVM::MemsetOp::loadsFrom(const MemorySlot &slot) { return false; }
1170
1171bool LLVM::MemsetOp::storesTo(const MemorySlot &slot) {
1172 return getDst() == slot.ptr;
1173}
1174
1175Value LLVM::MemsetOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1176 Value reachingDef,
1177 const DataLayout &dataLayout) {
1178 return memsetGetStored(*this, slot, builder);
1179}
1180
1181bool LLVM::MemsetOp::canUsesBeRemoved(
1182 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1183 SmallVectorImpl<OpOperand *> &newBlockingUses,
1184 const DataLayout &dataLayout) {
1185 return memsetCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1186 dataLayout);
1187}
1188
1189DeletionKind LLVM::MemsetOp::removeBlockingUses(
1190 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1191 OpBuilder &builder, Value reachingDefinition,
1192 const DataLayout &dataLayout) {
1193 return DeletionKind::Delete;
1194}
1195
1196LogicalResult LLVM::MemsetOp::ensureOnlySafeAccesses(
1197 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1198 const DataLayout &dataLayout) {
1199 return success(definitelyWritesOnlyWithinSlot(*this, slot, dataLayout));
1200}
1201
1202bool LLVM::MemsetOp::canRewire(const DestructurableMemorySlot &slot,
1203 SmallPtrSetImpl<Attribute> &usedIndices,
1204 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1205 const DataLayout &dataLayout) {
1206 return memsetCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1207 dataLayout);
1208}
1209
1210DeletionKind LLVM::MemsetOp::rewire(const DestructurableMemorySlot &slot,
1212 OpBuilder &builder,
1213 const DataLayout &dataLayout) {
1214 return memsetRewire(*this, slot, subslots, builder, dataLayout);
1215}
1216
1217bool LLVM::MemsetInlineOp::loadsFrom(const MemorySlot &slot) { return false; }
1218
1219bool LLVM::MemsetInlineOp::storesTo(const MemorySlot &slot) {
1220 return getDst() == slot.ptr;
1221}
1222
1223Value LLVM::MemsetInlineOp::getStored(const MemorySlot &slot,
1224 OpBuilder &builder, Value reachingDef,
1225 const DataLayout &dataLayout) {
1226 return memsetGetStored(*this, slot, builder);
1227}
1228
1229bool LLVM::MemsetInlineOp::canUsesBeRemoved(
1230 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1231 SmallVectorImpl<OpOperand *> &newBlockingUses,
1232 const DataLayout &dataLayout) {
1233 return memsetCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1234 dataLayout);
1235}
1236
1237DeletionKind LLVM::MemsetInlineOp::removeBlockingUses(
1238 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1239 OpBuilder &builder, Value reachingDefinition,
1240 const DataLayout &dataLayout) {
1241 return DeletionKind::Delete;
1242}
1243
1244LogicalResult LLVM::MemsetInlineOp::ensureOnlySafeAccesses(
1245 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1246 const DataLayout &dataLayout) {
1247 return success(definitelyWritesOnlyWithinSlot(*this, slot, dataLayout));
1248}
1249
1250bool LLVM::MemsetInlineOp::canRewire(
1251 const DestructurableMemorySlot &slot,
1252 SmallPtrSetImpl<Attribute> &usedIndices,
1253 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1254 const DataLayout &dataLayout) {
1255 return memsetCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1256 dataLayout);
1257}
1258
1260LLVM::MemsetInlineOp::rewire(const DestructurableMemorySlot &slot,
1262 OpBuilder &builder, const DataLayout &dataLayout) {
1263 return memsetRewire(*this, slot, subslots, builder, dataLayout);
1264}
1265
1266//===----------------------------------------------------------------------===//
1267// Interfaces for memcpy/memmove
1268//===----------------------------------------------------------------------===//
1269
1270template <class MemcpyLike>
1271static bool memcpyLoadsFrom(MemcpyLike op, const MemorySlot &slot) {
1272 return op.getSrc() == slot.ptr;
1273}
1274
1275template <class MemcpyLike>
1276static bool memcpyStoresTo(MemcpyLike op, const MemorySlot &slot) {
1277 return op.getDst() == slot.ptr;
1278}
1279
1280template <class MemcpyLike>
1281static Value memcpyGetStored(MemcpyLike op, const MemorySlot &slot,
1282 OpBuilder &builder) {
1283 return LLVM::LoadOp::create(builder, op.getLoc(), slot.elemType, op.getSrc());
1284}
1285
1286template <class MemcpyLike>
1287static bool
1288memcpyCanUsesBeRemoved(MemcpyLike op, const MemorySlot &slot,
1289 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1290 SmallVectorImpl<OpOperand *> &newBlockingUses,
1291 const DataLayout &dataLayout) {
1292 // If source and destination are the same, memcpy behavior is undefined and
1293 // memmove is a no-op. Because there is no memory change happening here,
1294 // simplifying such operations is left to canonicalization.
1295 if (op.getDst() == op.getSrc())
1296 return false;
1297
1298 if (op.getIsVolatile())
1299 return false;
1300
1301 return getStaticMemIntrLen(op) == dataLayout.getTypeSize(slot.elemType);
1302}
1303
1304template <class MemcpyLike>
1305static DeletionKind
1306memcpyRemoveBlockingUses(MemcpyLike op, const MemorySlot &slot,
1307 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1308 OpBuilder &builder, Value reachingDefinition) {
1309 if (op.loadsFrom(slot))
1310 LLVM::StoreOp::create(builder, op.getLoc(), reachingDefinition,
1311 op.getDst());
1312 return DeletionKind::Delete;
1313}
1314
1315template <class MemcpyLike>
1316static LogicalResult
1317memcpyEnsureOnlySafeAccesses(MemcpyLike op, const MemorySlot &slot,
1318 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed) {
1319 DataLayout dataLayout = DataLayout::closest(op);
1320 // While rewiring memcpy-like intrinsics only supports full copies, partial
1321 // copies are still safe accesses so it is enough to only check for writes
1322 // within bounds.
1323 return success(definitelyWritesOnlyWithinSlot(op, slot, dataLayout));
1324}
1325
1326template <class MemcpyLike>
1327static bool memcpyCanRewire(MemcpyLike op, const DestructurableMemorySlot &slot,
1328 SmallPtrSetImpl<Attribute> &usedIndices,
1329 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1330 const DataLayout &dataLayout) {
1331 if (op.getIsVolatile())
1332 return false;
1333
1334 if (!cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap())
1335 return false;
1336
1337 if (!areAllIndicesI32(slot))
1338 return false;
1339
1340 // Only full copies are supported.
1341 if (getStaticMemIntrLen(op) != dataLayout.getTypeSize(slot.elemType))
1342 return false;
1343
1344 if (op.getSrc() == slot.ptr)
1345 usedIndices.insert_range(llvm::make_first_range(slot.subelementTypes));
1346
1347 return true;
1348}
1349
1350namespace {
1351
1352template <class MemcpyLike>
1353void createMemcpyLikeToReplace(OpBuilder &builder, const DataLayout &layout,
1354 MemcpyLike toReplace, Value dst, Value src,
1355 Type toCpy, bool isVolatile) {
1356 Value memcpySize =
1357 LLVM::ConstantOp::create(builder, toReplace.getLoc(),
1358 IntegerAttr::get(toReplace.getLen().getType(),
1359 layout.getTypeSize(toCpy)));
1360 MemcpyLike::create(builder, toReplace.getLoc(), dst, src, memcpySize,
1361 isVolatile);
1362}
1363
1364template <>
1365void createMemcpyLikeToReplace(OpBuilder &builder, const DataLayout &layout,
1366 LLVM::MemcpyInlineOp toReplace, Value dst,
1367 Value src, Type toCpy, bool isVolatile) {
1368 Type lenType = IntegerType::get(toReplace->getContext(),
1369 toReplace.getLen().getBitWidth());
1370 LLVM::MemcpyInlineOp::create(
1371 builder, toReplace.getLoc(), dst, src,
1372 IntegerAttr::get(lenType, layout.getTypeSize(toCpy)), isVolatile);
1373}
1374
1375} // namespace
1376
1377/// Rewires a memcpy-like operation. Only copies to or from the full slot are
1378/// supported.
1379template <class MemcpyLike>
1380static DeletionKind
1381memcpyRewire(MemcpyLike op, const DestructurableMemorySlot &slot,
1382 DenseMap<Attribute, MemorySlot> &subslots, OpBuilder &builder,
1383 const DataLayout &dataLayout) {
1384 if (subslots.empty())
1385 return DeletionKind::Delete;
1386
1387 assert((slot.ptr == op.getDst()) != (slot.ptr == op.getSrc()));
1388 bool isDst = slot.ptr == op.getDst();
1389
1390#ifndef NDEBUG
1391 size_t slotsTreated = 0;
1392#endif
1393
1394 // It was previously checked that index types are consistent, so this type can
1395 // be fetched now.
1396 Type indexType = cast<IntegerAttr>(subslots.begin()->first).getType();
1397 for (size_t i = 0, e = slot.subelementTypes.size(); i != e; i++) {
1398 Attribute index = IntegerAttr::get(indexType, i);
1399 if (!subslots.contains(index))
1400 continue;
1401 const MemorySlot &subslot = subslots.at(index);
1402
1403#ifndef NDEBUG
1404 slotsTreated++;
1405#endif
1406
1407 // First get a pointer to the equivalent of this subslot from the source
1408 // pointer.
1409 SmallVector<LLVM::GEPArg> gepIndices{
1410 0, static_cast<int32_t>(
1411 cast<IntegerAttr>(index).getValue().getZExtValue())};
1412 Value subslotPtrInOther = LLVM::GEPOp::create(
1413 builder, op.getLoc(), LLVM::LLVMPointerType::get(op.getContext()),
1414 slot.elemType, isDst ? op.getSrc() : op.getDst(), gepIndices);
1415
1416 // Then create a new memcpy out of this source pointer.
1417 createMemcpyLikeToReplace(builder, dataLayout, op,
1418 isDst ? subslot.ptr : subslotPtrInOther,
1419 isDst ? subslotPtrInOther : subslot.ptr,
1420 subslot.elemType, op.getIsVolatile());
1421 }
1422
1423 assert(subslots.size() == slotsTreated);
1424
1425 return DeletionKind::Delete;
1426}
1427
1428bool LLVM::MemcpyOp::loadsFrom(const MemorySlot &slot) {
1429 return memcpyLoadsFrom(*this, slot);
1430}
1431
1432bool LLVM::MemcpyOp::storesTo(const MemorySlot &slot) {
1433 return memcpyStoresTo(*this, slot);
1434}
1435
1436Value LLVM::MemcpyOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1437 Value reachingDef,
1438 const DataLayout &dataLayout) {
1439 return memcpyGetStored(*this, slot, builder);
1440}
1441
1442bool LLVM::MemcpyOp::canUsesBeRemoved(
1443 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1444 SmallVectorImpl<OpOperand *> &newBlockingUses,
1445 const DataLayout &dataLayout) {
1446 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1447 dataLayout);
1448}
1449
1450DeletionKind LLVM::MemcpyOp::removeBlockingUses(
1451 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1452 OpBuilder &builder, Value reachingDefinition,
1453 const DataLayout &dataLayout) {
1454 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1455 reachingDefinition);
1456}
1457
1458LogicalResult LLVM::MemcpyOp::ensureOnlySafeAccesses(
1459 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1460 const DataLayout &dataLayout) {
1461 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1462}
1463
1464bool LLVM::MemcpyOp::canRewire(const DestructurableMemorySlot &slot,
1465 SmallPtrSetImpl<Attribute> &usedIndices,
1466 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1467 const DataLayout &dataLayout) {
1468 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1469 dataLayout);
1470}
1471
1472DeletionKind LLVM::MemcpyOp::rewire(const DestructurableMemorySlot &slot,
1474 OpBuilder &builder,
1475 const DataLayout &dataLayout) {
1476 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1477}
1478
1479bool LLVM::MemcpyInlineOp::loadsFrom(const MemorySlot &slot) {
1480 return memcpyLoadsFrom(*this, slot);
1481}
1482
1483bool LLVM::MemcpyInlineOp::storesTo(const MemorySlot &slot) {
1484 return memcpyStoresTo(*this, slot);
1485}
1486
1487Value LLVM::MemcpyInlineOp::getStored(const MemorySlot &slot,
1488 OpBuilder &builder, Value reachingDef,
1489 const DataLayout &dataLayout) {
1490 return memcpyGetStored(*this, slot, builder);
1491}
1492
1493bool LLVM::MemcpyInlineOp::canUsesBeRemoved(
1494 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1495 SmallVectorImpl<OpOperand *> &newBlockingUses,
1496 const DataLayout &dataLayout) {
1497 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1498 dataLayout);
1499}
1500
1501DeletionKind LLVM::MemcpyInlineOp::removeBlockingUses(
1502 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1503 OpBuilder &builder, Value reachingDefinition,
1504 const DataLayout &dataLayout) {
1505 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1506 reachingDefinition);
1507}
1508
1509LogicalResult LLVM::MemcpyInlineOp::ensureOnlySafeAccesses(
1510 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1511 const DataLayout &dataLayout) {
1512 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1513}
1514
1515bool LLVM::MemcpyInlineOp::canRewire(
1516 const DestructurableMemorySlot &slot,
1517 SmallPtrSetImpl<Attribute> &usedIndices,
1518 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1519 const DataLayout &dataLayout) {
1520 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1521 dataLayout);
1522}
1523
1525LLVM::MemcpyInlineOp::rewire(const DestructurableMemorySlot &slot,
1527 OpBuilder &builder, const DataLayout &dataLayout) {
1528 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1529}
1530
1531bool LLVM::MemmoveOp::loadsFrom(const MemorySlot &slot) {
1532 return memcpyLoadsFrom(*this, slot);
1533}
1534
1535bool LLVM::MemmoveOp::storesTo(const MemorySlot &slot) {
1536 return memcpyStoresTo(*this, slot);
1537}
1538
1539Value LLVM::MemmoveOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1540 Value reachingDef,
1541 const DataLayout &dataLayout) {
1542 return memcpyGetStored(*this, slot, builder);
1543}
1544
1545bool LLVM::MemmoveOp::canUsesBeRemoved(
1546 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1547 SmallVectorImpl<OpOperand *> &newBlockingUses,
1548 const DataLayout &dataLayout) {
1549 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1550 dataLayout);
1551}
1552
1553DeletionKind LLVM::MemmoveOp::removeBlockingUses(
1554 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1555 OpBuilder &builder, Value reachingDefinition,
1556 const DataLayout &dataLayout) {
1557 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1558 reachingDefinition);
1559}
1560
1561LogicalResult LLVM::MemmoveOp::ensureOnlySafeAccesses(
1562 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1563 const DataLayout &dataLayout) {
1564 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1565}
1566
1567bool LLVM::MemmoveOp::canRewire(const DestructurableMemorySlot &slot,
1568 SmallPtrSetImpl<Attribute> &usedIndices,
1569 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1570 const DataLayout &dataLayout) {
1571 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1572 dataLayout);
1573}
1574
1575DeletionKind LLVM::MemmoveOp::rewire(const DestructurableMemorySlot &slot,
1577 OpBuilder &builder,
1578 const DataLayout &dataLayout) {
1579 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1580}
1581
1582//===----------------------------------------------------------------------===//
1583// Interfaces for destructurable types
1584//===----------------------------------------------------------------------===//
1585
1586std::optional<DenseMap<Attribute, Type>>
1587LLVM::LLVMStructType::getSubelementIndexMap() const {
1588 Type i32 = IntegerType::get(getContext(), 32);
1589 DenseMap<Attribute, Type> destructured;
1590 for (const auto &[index, elemType] : llvm::enumerate(getBody()))
1591 destructured.insert({IntegerAttr::get(i32, index), elemType});
1592 return destructured;
1593}
1594
1595Type LLVM::LLVMStructType::getTypeAtIndex(Attribute index) const {
1596 auto indexAttr = llvm::dyn_cast<IntegerAttr>(index);
1597 if (!indexAttr || !indexAttr.getType().isInteger(32))
1598 return {};
1599 int32_t indexInt = indexAttr.getInt();
1600 ArrayRef<Type> body = getBody();
1601 if (indexInt < 0 || body.size() <= static_cast<uint32_t>(indexInt))
1602 return {};
1603 return body[indexInt];
1604}
1605
1606std::optional<DenseMap<Attribute, Type>>
1607LLVM::LLVMArrayType::getSubelementIndexMap() const {
1608 constexpr size_t maxArraySizeForDestructuring = 16;
1609 if (getNumElements() > maxArraySizeForDestructuring)
1610 return {};
1611 int32_t numElements = getNumElements();
1612
1613 Type i32 = IntegerType::get(getContext(), 32);
1614 DenseMap<Attribute, Type> destructured;
1615 for (int32_t index = 0; index < numElements; ++index)
1616 destructured.insert({IntegerAttr::get(i32, index), getElementType()});
1617 return destructured;
1618}
1619
1620Type LLVM::LLVMArrayType::getTypeAtIndex(Attribute index) const {
1621 auto indexAttr = llvm::dyn_cast<IntegerAttr>(index);
1622 if (!indexAttr || !indexAttr.getType().isInteger(32))
1623 return {};
1624 int32_t indexInt = indexAttr.getInt();
1625 if (indexInt < 0 || getNumElements() <= static_cast<uint32_t>(indexInt))
1626 return {};
1627 return getElementType();
1628}
return success()
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
static Type getElementType(Type type)
Determine the element type of type.
static int64_t getNumElements(Type t)
Compute the total number of elements in the given type, also taking into account nested types.
static LLVM::LLVMArrayType getByteArrayType(MLIRContext *context, unsigned size)
Constructs a byte array type of the given size.
static LogicalResult memcpyEnsureOnlySafeAccesses(MemcpyLike op, const MemorySlot &slot, SmallVectorImpl< MemorySlot > &mustBeSafelyUsed)
static std::optional< SubslotAccessInfo > getSubslotAccessInfo(const DestructurableMemorySlot &slot, const DataLayout &dataLayout, LLVM::GEPOp gep)
Computes subslot access information for an access into slot with the given offset.
static bool areAllIndicesI32(const DestructurableMemorySlot &slot)
Checks whether all indices are i32.
static Value castToSameSizedInt(OpBuilder &builder, Location loc, Value val, const DataLayout &dataLayout)
Converts a value to an integer type of the same size.
static Value castSameSizedTypes(OpBuilder &builder, Location loc, Value srcValue, Type targetType, const DataLayout &dataLayout)
Constructs operations that convert srcValue into a new value of type targetType.
static bool memcpyStoresTo(MemcpyLike op, const MemorySlot &slot)
static DeletionKind memsetRewire(MemsetIntr op, const DestructurableMemorySlot &slot, DenseMap< Attribute, MemorySlot > &subslots, OpBuilder &builder, const DataLayout &dataLayout)
static Type getTypeAtIndex(const DestructurableMemorySlot &slot, Attribute index)
Returns the subslot's type at the requested index.
static bool areConversionCompatible(const DataLayout &layout, Type targetType, Type srcType, bool narrowingConversion)
Checks that rhs can be converted to lhs by a sequence of casts and truncations.
static bool forwardToUsers(Operation *op, SmallVectorImpl< OpOperand * > &newBlockingUses)
Conditions the deletion of the operation to the removal of all its uses.
static bool memsetCanUsesBeRemoved(MemsetIntr op, const MemorySlot &slot, const SmallPtrSetImpl< OpOperand * > &blockingUses, SmallVectorImpl< OpOperand * > &newBlockingUses, const DataLayout &dataLayout)
static bool memcpyLoadsFrom(MemcpyLike op, const MemorySlot &slot)
static bool isSupportedTypeForConversion(Type type)
Checks if type can be used in any kind of conversion sequences.
static Value createExtractAndCast(OpBuilder &builder, Location loc, Value srcValue, Type targetType, const DataLayout &dataLayout)
Constructs operations that convert srcValue into a new value of type targetType.
static Value createInsertAndCast(OpBuilder &builder, Location loc, Value srcValue, Value reachingDef, const DataLayout &dataLayout)
Constructs operations that insert the bits of srcValue into the "beginning" of reachingDef (beginning...
static DeletionKind memcpyRemoveBlockingUses(MemcpyLike op, const MemorySlot &slot, const SmallPtrSetImpl< OpOperand * > &blockingUses, OpBuilder &builder, Value reachingDefinition)
static bool memcpyCanUsesBeRemoved(MemcpyLike op, const MemorySlot &slot, const SmallPtrSetImpl< OpOperand * > &blockingUses, SmallVectorImpl< OpOperand * > &newBlockingUses, const DataLayout &dataLayout)
static bool isBigEndian(const DataLayout &dataLayout)
Checks if dataLayout describes a little endian layout.
static std::optional< uint64_t > gepToByteOffset(const DataLayout &dataLayout, LLVM::GEPOp gep)
Returns the amount of bytes the provided GEP elements will offset the pointer by.
static bool hasAllZeroIndices(LLVM::GEPOp gepOp)
static bool isValidAccessType(const MemorySlot &slot, Type accessType, const DataLayout &dataLayout)
Checks if slot can be accessed through the provided access type.
static Value memcpyGetStored(MemcpyLike op, const MemorySlot &slot, OpBuilder &builder)
static Value castIntValueToSameSizedType(OpBuilder &builder, Location loc, Value val, Type targetType)
Converts a value with an integer type to targetType.
static bool memsetCanRewire(MemsetIntr op, const DestructurableMemorySlot &slot, SmallPtrSetImpl< Attribute > &usedIndices, SmallVectorImpl< MemorySlot > &mustBeSafelyUsed, const DataLayout &dataLayout)
static DeletionKind memcpyRewire(MemcpyLike op, const DestructurableMemorySlot &slot, DenseMap< Attribute, MemorySlot > &subslots, OpBuilder &builder, const DataLayout &dataLayout)
Rewires a memcpy-like operation.
static Value memsetGetStored(MemsetIntr op, const MemorySlot &slot, OpBuilder &builder)
static bool definitelyWritesOnlyWithinSlot(MemIntr op, const MemorySlot &slot, const DataLayout &dataLayout)
Returns whether one can be sure the memory intrinsic does not write outside of the bounds of the give...
static bool memcpyCanRewire(MemcpyLike op, const DestructurableMemorySlot &slot, SmallPtrSetImpl< Attribute > &usedIndices, SmallVectorImpl< MemorySlot > &mustBeSafelyUsed, const DataLayout &dataLayout)
b getContext())
*if copies could not be generated due to yet unimplemented cases *copyInPlacementStart and copyOutPlacementStart in copyPlacementBlock *specify the insertion points where the incoming copies and outgoing should be the output argument nBegin is set to its * replacement(set to `begin` if no invalidation happens). Since outgoing *copies could have been inserted at `end`
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class represents an argument of a Block.
Definition Value.h:309
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:228
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:67
MLIRContext * getContext() const
Definition Builders.h:56
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.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
Attribute getEndianness() const
Returns the specified endianness.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:207
void setInsertionPoint(Block *block, Block::iterator insertPoint)
Set the insertion point to the specified location.
Definition Builders.h:398
void createOrFold(SmallVectorImpl< Value > &results, Location location, Args &&...args)
Create an operation of specific op type at the current insertion point, and immediately try to fold i...
Definition Builders.h:526
void setInsertionPointAfter(Operation *op)
Sets the insertion point to the node after the specified operation, which will cause subsequent inser...
Definition Builders.h:412
This class represents an operand of an operation.
Definition Value.h:257
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
result_range getResults()
Definition Operation.h:415
void erase()
Remove this operation from its parent block and delete it.
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
Dialect & getDialect() const
Get the dialect this type is registered to.
Definition Types.h:107
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
bool use_empty() const
Returns true if this value has no uses.
Definition Value.h:208
MLIRContext * getContext() const
Utility to get the associated MLIRContext that this value is defined in.
Definition Value.h:108
Type getType() const
Return the type of this value.
Definition Value.h:105
Operation * getDefiningOp() const
If this value is the result of an operation, return the operation that defines it.
Definition Value.cpp:18
constexpr int kGEPConstantBitWidth
Bit-width of a 'GEPConstantIndex' within GEPArg.
Definition LLVMDialect.h:62
Include the generated interface declarations.
bool matchPattern(Value value, const Pattern &pattern)
Entry point for matching a pattern over a Value.
Definition Matchers.h:490
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
detail::constant_int_predicate_matcher m_One()
Matches a constant scalar / vector splat / tensor splat integer one.
Definition Matchers.h:478
DeletionKind
Returned by operation promotion logic requesting the deletion of an operation.
@ Keep
Keep the operation after promotion.
@ Delete
Delete the operation after promotion.
llvm::TypeSwitch< T, ResultT > TypeSwitch
Definition LLVM.h:144
llvm::DenseMap< KeyT, ValueT, KeyInfoT, BucketT > DenseMap
Definition LLVM.h:126
detail::constant_op_matcher m_Constant()
Matches a constant foldable operation.
Definition Matchers.h:369
Memory slot attached with information about its destructuring procedure.
DenseMap< Attribute, Type > subelementTypes
Maps an index within the memory slot to the corresponding subelement type.
Represents a slot in memory.
Value ptr
Pointer to the memory slot, used by operations to refer to it.
Type elemType
Type of the value contained in the slot.