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