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