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::DbgDeclareOp::canUsesBeRemoved(
623 const SmallPtrSetImpl<OpOperand *> &blockingUses,
624 SmallVectorImpl<OpOperand *> &newBlockingUses,
625 const DataLayout &dataLayout) {
626 return true;
627}
628
629DeletionKind LLVM::DbgDeclareOp::removeBlockingUses(
630 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
632}
633
634bool LLVM::DbgValueOp::canUsesBeRemoved(
635 const SmallPtrSetImpl<OpOperand *> &blockingUses,
636 SmallVectorImpl<OpOperand *> &newBlockingUses,
637 const DataLayout &dataLayout) {
638 // There is only one operand that we can remove the use of.
639 if (blockingUses.size() != 1)
640 return false;
641
642 return (*blockingUses.begin())->get() == getValue();
643}
644
645DeletionKind LLVM::DbgValueOp::removeBlockingUses(
646 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
647 // builder by default is after '*this', but we need it before '*this'.
648 builder.setInsertionPoint(*this);
649
650 // Rather than dropping the debug value, replace it with undef to preserve the
651 // debug local variable info. This allows the debugger to inform the user that
652 // the variable has been optimized out.
653 auto undef =
654 UndefOp::create(builder, getValue().getLoc(), getValue().getType());
655 getValueMutable().assign(undef);
656 return DeletionKind::Keep;
657}
658
659bool LLVM::DbgDeclareOp::requiresReplacedValues() { return true; }
660
661void LLVM::DbgDeclareOp::visitReplacedValues(
662 ArrayRef<std::pair<Operation *, Value>> definitions, OpBuilder &builder) {
663 for (auto [op, value] : definitions) {
664 builder.setInsertionPointAfter(op);
665 LLVM::DbgValueOp::create(builder, getLoc(), value, getVarInfo(),
666 getLocationExpr());
667 }
668}
669
670//===----------------------------------------------------------------------===//
671// Interfaces for GEPOp
672//===----------------------------------------------------------------------===//
673
674static bool hasAllZeroIndices(LLVM::GEPOp gepOp) {
675 return llvm::all_of(gepOp.getIndices(), [](auto index) {
676 auto indexAttr = llvm::dyn_cast_if_present<IntegerAttr>(index);
677 return indexAttr && indexAttr.getValue() == 0;
678 });
679}
680
681bool LLVM::GEPOp::canUsesBeRemoved(
682 const SmallPtrSetImpl<OpOperand *> &blockingUses,
683 SmallVectorImpl<OpOperand *> &newBlockingUses,
684 const DataLayout &dataLayout) {
685 // GEP can be removed as long as it is a no-op and its users can be removed.
686 // `inrange` is only valid on constant GEP expressions, so an inrange GEP on
687 // an alloca is illegal and we bail out.
688 if (getInrangeAttr() || !hasAllZeroIndices(*this))
689 return false;
690 return forwardToUsers(*this, newBlockingUses);
691}
692
693DeletionKind LLVM::GEPOp::removeBlockingUses(
694 const SmallPtrSetImpl<OpOperand *> &blockingUses, OpBuilder &builder) {
696}
697
698/// Returns the amount of bytes the provided GEP elements will offset the
699/// pointer by. Returns nullopt if no constant offset could be computed.
700static std::optional<uint64_t> gepToByteOffset(const DataLayout &dataLayout,
701 LLVM::GEPOp gep) {
702 // Collects all indices.
704 for (auto index : gep.getIndices()) {
705 auto constIndex = dyn_cast<IntegerAttr>(index);
706 if (!constIndex)
707 return {};
708 int64_t gepIndex = constIndex.getInt();
709 // Negative indices are not supported.
710 if (gepIndex < 0)
711 return {};
712 indices.push_back(gepIndex);
713 }
714
715 Type currentType = gep.getElemType();
716 uint64_t offset = indices[0] * dataLayout.getTypeSize(currentType);
717
718 for (uint64_t index : llvm::drop_begin(indices)) {
719 bool shouldCancel =
720 TypeSwitch<Type, bool>(currentType)
721 .Case([&](LLVM::LLVMArrayType arrayType) {
722 offset +=
723 index * dataLayout.getTypeSize(arrayType.getElementType());
724 currentType = arrayType.getElementType();
725 return false;
726 })
727 .Case([&](LLVM::LLVMStructType structType) {
728 ArrayRef<Type> body = structType.getBody();
729 assert(index < body.size() && "expected valid struct indexing");
730 for (uint32_t i : llvm::seq(index)) {
731 if (!structType.isPacked())
732 offset = llvm::alignTo(
733 offset, dataLayout.getTypeABIAlignment(body[i]));
734 offset += dataLayout.getTypeSize(body[i]);
735 }
736
737 // Align for the current type as well.
738 if (!structType.isPacked())
739 offset = llvm::alignTo(
740 offset, dataLayout.getTypeABIAlignment(body[index]));
741 currentType = body[index];
742 return false;
743 })
744 .Default([&](Type type) {
745 LDBG() << "[sroa] Unsupported type for offset computations"
746 << type;
747 return true;
748 });
749
750 if (shouldCancel)
751 return std::nullopt;
752 }
753
754 return offset;
755}
756
757namespace {
758/// A struct that stores both the index into the aggregate type of the slot as
759/// well as the corresponding byte offset in memory.
760struct SubslotAccessInfo {
761 /// The parent slot's index that the access falls into.
762 uint32_t index;
763 /// The offset into the subslot of the access.
764 uint64_t subslotOffset;
765};
766} // namespace
767
768/// Computes subslot access information for an access into `slot` with the given
769/// offset.
770/// Returns nullopt when the offset is out-of-bounds or when the access is into
771/// the padding of `slot`.
772static std::optional<SubslotAccessInfo>
774 const DataLayout &dataLayout, LLVM::GEPOp gep) {
775 std::optional<uint64_t> offset = gepToByteOffset(dataLayout, gep);
776 if (!offset)
777 return {};
778
779 // Helper to check that a constant index is in the bounds of the GEP index
780 // representation. LLVM dialects's GEP arguments have a limited bitwidth, thus
781 // this additional check is necessary.
782 auto isOutOfBoundsGEPIndex = [](uint64_t index) {
783 return index >= (1 << LLVM::kGEPConstantBitWidth);
784 };
785
786 Type type = slot.elemType;
787 if (*offset >= dataLayout.getTypeSize(type))
788 return {};
790 .Case([&](LLVM::LLVMArrayType arrayType)
791 -> std::optional<SubslotAccessInfo> {
792 // Find which element of the array contains the offset.
793 uint64_t elemSize = dataLayout.getTypeSize(arrayType.getElementType());
794 uint64_t index = *offset / elemSize;
795 if (isOutOfBoundsGEPIndex(index))
796 return {};
797 return SubslotAccessInfo{static_cast<uint32_t>(index),
798 *offset - (index * elemSize)};
799 })
800 .Case([&](LLVM::LLVMStructType structType)
801 -> std::optional<SubslotAccessInfo> {
802 uint64_t distanceToStart = 0;
803 // Walk over the elements of the struct to find in which of
804 // them the offset is.
805 for (auto [index, elem] : llvm::enumerate(structType.getBody())) {
806 uint64_t elemSize = dataLayout.getTypeSize(elem);
807 if (!structType.isPacked()) {
808 distanceToStart = llvm::alignTo(
809 distanceToStart, dataLayout.getTypeABIAlignment(elem));
810 // If the offset is in padding, cancel the rewrite.
811 if (offset < distanceToStart)
812 return {};
813 }
814
815 if (offset < distanceToStart + elemSize) {
816 if (isOutOfBoundsGEPIndex(index))
817 return {};
818 // The offset is within this element, stop iterating the
819 // struct and return the index.
820 return SubslotAccessInfo{static_cast<uint32_t>(index),
821 *offset - distanceToStart};
822 }
823
824 // The offset is not within this element, continue walking
825 // over the struct.
826 distanceToStart += elemSize;
827 }
828
829 return {};
830 });
831}
832
833/// Constructs a byte array type of the given size.
834static LLVM::LLVMArrayType getByteArrayType(MLIRContext *context,
835 unsigned size) {
836 auto byteType = IntegerType::get(context, 8);
837 return LLVM::LLVMArrayType::get(context, byteType, size);
838}
839
840LogicalResult LLVM::GEPOp::ensureOnlySafeAccesses(
841 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
842 const DataLayout &dataLayout) {
843 if (getBase() != slot.ptr)
844 return success();
845 std::optional<uint64_t> gepOffset = gepToByteOffset(dataLayout, *this);
846 if (!gepOffset)
847 return failure();
848 uint64_t slotSize = dataLayout.getTypeSize(slot.elemType);
849 // Check that the access is strictly inside the slot.
850 if (*gepOffset >= slotSize)
851 return failure();
852 // Every access that remains in bounds of the remaining slot is considered
853 // legal.
854 mustBeSafelyUsed.emplace_back<MemorySlot>(
855 {getRes(), getByteArrayType(getContext(), slotSize - *gepOffset)});
856 return success();
857}
858
859bool LLVM::GEPOp::canRewire(const DestructurableMemorySlot &slot,
860 SmallPtrSetImpl<Attribute> &usedIndices,
861 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
862 const DataLayout &dataLayout) {
863 if (!isa<LLVM::LLVMPointerType>(getBase().getType()))
864 return false;
865
866 if (getBase() != slot.ptr)
867 return false;
868 // `inrange` is only valid on constant GEP expressions, so an inrange GEP on
869 // an alloca is illegal and SROA bails out.
870 if (getInrangeAttr())
871 return false;
872 std::optional<SubslotAccessInfo> accessInfo =
873 getSubslotAccessInfo(slot, dataLayout, *this);
874 if (!accessInfo)
875 return false;
876 auto indexAttr =
877 IntegerAttr::get(IntegerType::get(getContext(), 32), accessInfo->index);
878 assert(slot.subelementTypes.contains(indexAttr));
879 usedIndices.insert(indexAttr);
880
881 // The remainder of the subslot should be accesses in-bounds. Thus, we create
882 // a dummy slot with the size of the remainder.
883 Type subslotType = slot.subelementTypes.lookup(indexAttr);
884 uint64_t slotSize = dataLayout.getTypeSize(subslotType);
885 LLVM::LLVMArrayType remainingSlotType =
886 getByteArrayType(getContext(), slotSize - accessInfo->subslotOffset);
887 mustBeSafelyUsed.emplace_back<MemorySlot>({getRes(), remainingSlotType});
888
889 return true;
890}
891
892DeletionKind LLVM::GEPOp::rewire(const DestructurableMemorySlot &slot,
894 OpBuilder &builder,
895 const DataLayout &dataLayout) {
896 std::optional<SubslotAccessInfo> accessInfo =
897 getSubslotAccessInfo(slot, dataLayout, *this);
898 assert(accessInfo && "expected access info to be checked before");
899 auto indexAttr =
900 IntegerAttr::get(IntegerType::get(getContext(), 32), accessInfo->index);
901 const MemorySlot &newSlot = subslots.at(indexAttr);
902
903 auto byteType = IntegerType::get(builder.getContext(), 8);
904 auto newPtr = builder.createOrFold<LLVM::GEPOp>(
905 getLoc(), getResult().getType(), byteType, newSlot.ptr,
906 ArrayRef<GEPArg>(accessInfo->subslotOffset), getNoWrapFlags());
907 getResult().replaceAllUsesWith(newPtr);
909}
910
911//===----------------------------------------------------------------------===//
912// Utilities for memory intrinsics
913//===----------------------------------------------------------------------===//
914
915namespace {
916
917/// Returns the length of the given memory intrinsic in bytes if it can be known
918/// at compile-time on a best-effort basis, nothing otherwise.
919template <class MemIntr>
920std::optional<uint64_t> getStaticMemIntrLen(MemIntr op) {
921 APInt memIntrLen;
922 if (!matchPattern(op.getLen(), m_ConstantInt(&memIntrLen)))
923 return {};
924 if (memIntrLen.getBitWidth() > 64)
925 return {};
926 return memIntrLen.getZExtValue();
927}
928
929/// Returns the length of the given memory intrinsic in bytes if it can be known
930/// at compile-time on a best-effort basis, nothing otherwise.
931/// Because MemcpyInlineOp has its length encoded as an attribute, this requires
932/// specialized handling.
933template <>
934std::optional<uint64_t> getStaticMemIntrLen(LLVM::MemcpyInlineOp op) {
935 APInt memIntrLen = op.getLen();
936 if (memIntrLen.getBitWidth() > 64)
937 return {};
938 return memIntrLen.getZExtValue();
939}
940
941/// Returns the length of the given memory intrinsic in bytes if it can be known
942/// at compile-time on a best-effort basis, nothing otherwise.
943/// Because MemsetInlineOp has its length encoded as an attribute, this requires
944/// specialized handling.
945template <>
946std::optional<uint64_t> getStaticMemIntrLen(LLVM::MemsetInlineOp op) {
947 APInt memIntrLen = op.getLen();
948 if (memIntrLen.getBitWidth() > 64)
949 return {};
950 return memIntrLen.getZExtValue();
951}
952
953/// Returns an integer attribute representing the length of a memset intrinsic
954template <class MemsetIntr>
955IntegerAttr createMemsetLenAttr(MemsetIntr op) {
956 IntegerAttr memsetLenAttr;
957 bool successfulMatch =
958 matchPattern(op.getLen(), m_Constant<IntegerAttr>(&memsetLenAttr));
959 (void)successfulMatch;
960 assert(successfulMatch);
961 return memsetLenAttr;
962}
963
964/// Returns an integer attribute representing the length of a memset intrinsic
965/// Because MemsetInlineOp has its length encoded as an attribute, this requires
966/// specialized handling.
967template <>
968IntegerAttr createMemsetLenAttr(LLVM::MemsetInlineOp op) {
969 return op.getLenAttr();
970}
971
972/// Creates a memset intrinsic of that matches the `toReplace` intrinsic
973/// using the provided parameters. There are template specializations for
974/// MemsetOp and MemsetInlineOp.
975template <class MemsetIntr>
976void createMemsetIntr(OpBuilder &builder, MemsetIntr toReplace,
977 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
980
981template <>
982void createMemsetIntr(OpBuilder &builder, LLVM::MemsetOp toReplace,
983 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
986 Value newMemsetSizeValue =
987 LLVM::ConstantOp::create(
988 builder, toReplace.getLen().getLoc(),
989 IntegerAttr::get(memsetLenAttr.getType(), newMemsetSize))
990 .getResult();
991
992 LLVM::MemsetOp::create(builder, toReplace.getLoc(), subslots.at(index).ptr,
993 toReplace.getVal(), newMemsetSizeValue,
994 toReplace.getIsVolatile());
995}
996
997template <>
998void createMemsetIntr(OpBuilder &builder, LLVM::MemsetInlineOp toReplace,
999 IntegerAttr memsetLenAttr, uint64_t newMemsetSize,
1001 Attribute index) {
1002 auto newMemsetSizeValue =
1003 IntegerAttr::get(memsetLenAttr.getType(), newMemsetSize);
1004
1005 LLVM::MemsetInlineOp::create(builder, toReplace.getLoc(),
1006 subslots.at(index).ptr, toReplace.getVal(),
1007 newMemsetSizeValue, toReplace.getIsVolatile());
1008}
1009
1010} // namespace
1011
1012/// Returns whether one can be sure the memory intrinsic does not write outside
1013/// of the bounds of the given slot, on a best-effort basis.
1014template <class MemIntr>
1015static bool definitelyWritesOnlyWithinSlot(MemIntr op, const MemorySlot &slot,
1016 const DataLayout &dataLayout) {
1017 if (!isa<LLVM::LLVMPointerType>(slot.ptr.getType()) ||
1018 op.getDst() != slot.ptr)
1019 return false;
1020
1021 std::optional<uint64_t> memIntrLen = getStaticMemIntrLen(op);
1022 return memIntrLen && *memIntrLen <= dataLayout.getTypeSize(slot.elemType);
1023}
1024
1025/// Checks whether all indices are i32. This is used to check GEPs can index
1026/// into them.
1028 Type i32 = IntegerType::get(slot.ptr.getContext(), 32);
1029 return llvm::all_of(llvm::make_first_range(slot.subelementTypes),
1030 [&](Attribute index) {
1031 auto intIndex = dyn_cast<IntegerAttr>(index);
1032 return intIndex && intIndex.getType() == i32;
1033 });
1034}
1035
1036//===----------------------------------------------------------------------===//
1037// Interfaces for memset and memset.inline
1038//===----------------------------------------------------------------------===//
1039
1040template <class MemsetIntr>
1041static bool memsetCanRewire(MemsetIntr op, const DestructurableMemorySlot &slot,
1042 SmallPtrSetImpl<Attribute> &usedIndices,
1043 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1044 const DataLayout &dataLayout) {
1045 if (&slot.elemType.getDialect() != op.getOperation()->getDialect())
1046 return false;
1047
1048 if (op.getIsVolatile())
1049 return false;
1050
1051 if (!cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap())
1052 return false;
1053
1054 if (!areAllIndicesI32(slot))
1055 return false;
1056
1057 return definitelyWritesOnlyWithinSlot(op, slot, dataLayout);
1058}
1059
1060template <class MemsetIntr>
1061static Value memsetGetStored(MemsetIntr op, const MemorySlot &slot,
1062 OpBuilder &builder) {
1063 /// Returns an integer value that is `width` bits wide representing the value
1064 /// assigned to the slot by memset.
1065 auto buildMemsetValue = [&](unsigned width) -> Value {
1066 assert(width % 8 == 0);
1067 auto intType = IntegerType::get(op.getContext(), width);
1068
1069 // If we know the pattern at compile time, we can compute and assign a
1070 // constant directly.
1071 IntegerAttr constantPattern;
1072 if (matchPattern(op.getVal(), m_Constant(&constantPattern))) {
1073 assert(constantPattern.getValue().getBitWidth() == 8);
1074 APInt memsetVal(/*numBits=*/width, /*val=*/0);
1075 for (unsigned loBit = 0; loBit < width; loBit += 8)
1076 memsetVal.insertBits(constantPattern.getValue(), loBit);
1077 return LLVM::ConstantOp::create(builder, op.getLoc(),
1078 IntegerAttr::get(intType, memsetVal));
1079 }
1080
1081 // If the output is a single byte, we can return the pattern directly.
1082 if (width == 8)
1083 return op.getVal();
1084
1085 // Otherwise build the memset integer at runtime by repeatedly shifting the
1086 // value and or-ing it with the previous value.
1087 uint64_t coveredBits = 8;
1088 Value currentValue =
1089 LLVM::ZExtOp::create(builder, op.getLoc(), intType, op.getVal());
1090 while (coveredBits < width) {
1091 Value shiftBy =
1092 LLVM::ConstantOp::create(builder, op.getLoc(), intType, coveredBits);
1093 Value shifted =
1094 LLVM::ShlOp::create(builder, op.getLoc(), currentValue, shiftBy);
1095 currentValue =
1096 LLVM::OrOp::create(builder, op.getLoc(), currentValue, shifted);
1097 coveredBits *= 2;
1098 }
1099
1100 return currentValue;
1101 };
1103 .Case([&](IntegerType type) -> Value {
1104 return buildMemsetValue(type.getWidth());
1105 })
1106 .Case([&](FloatType type) -> Value {
1107 Value intVal = buildMemsetValue(type.getWidth());
1108 return LLVM::BitcastOp::create(builder, op.getLoc(), type, intVal);
1109 })
1110 .DefaultUnreachable(
1111 "getStored should not be called on memset to unsupported type");
1112}
1113
1114template <class MemsetIntr>
1115static bool
1116memsetCanUsesBeRemoved(MemsetIntr op, const MemorySlot &slot,
1117 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1118 SmallVectorImpl<OpOperand *> &newBlockingUses,
1119 const DataLayout &dataLayout) {
1120 bool canConvertType =
1122 .Case<IntegerType, FloatType>([](auto type) {
1123 return type.getWidth() % 8 == 0 && type.getWidth() > 0;
1124 })
1125 .Default(false);
1126 if (!canConvertType)
1127 return false;
1128
1129 if (op.getIsVolatile())
1130 return false;
1131
1132 return getStaticMemIntrLen(op) == dataLayout.getTypeSize(slot.elemType);
1133}
1134
1135template <class MemsetIntr>
1136static DeletionKind
1137memsetRewire(MemsetIntr op, const DestructurableMemorySlot &slot,
1138 DenseMap<Attribute, MemorySlot> &subslots, OpBuilder &builder,
1139 const DataLayout &dataLayout) {
1140
1141 std::optional<DenseMap<Attribute, Type>> types =
1142 cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap();
1143
1144 IntegerAttr memsetLenAttr = createMemsetLenAttr(op);
1145
1146 bool packed = false;
1147 if (auto structType = dyn_cast<LLVM::LLVMStructType>(slot.elemType))
1148 packed = structType.isPacked();
1149
1150 Type i32 = IntegerType::get(op.getContext(), 32);
1151 uint64_t memsetLen = memsetLenAttr.getValue().getZExtValue();
1152 uint64_t covered = 0;
1153 for (size_t i = 0; i < types->size(); i++) {
1154 // Create indices on the fly to get elements in the right order.
1155 Attribute index = IntegerAttr::get(i32, i);
1156 Type elemType = types->at(index);
1157 uint64_t typeSize = dataLayout.getTypeSize(elemType);
1158
1159 if (!packed)
1160 covered =
1161 llvm::alignTo(covered, dataLayout.getTypeABIAlignment(elemType));
1162
1163 if (covered >= memsetLen)
1164 break;
1165
1166 // If this subslot is used, apply a new memset to it.
1167 // Otherwise, only compute its offset within the original memset.
1168 if (subslots.contains(index)) {
1169 uint64_t newMemsetSize = std::min(memsetLen - covered, typeSize);
1170 createMemsetIntr(builder, op, memsetLenAttr, newMemsetSize, subslots,
1171 index);
1172 }
1173
1174 covered += typeSize;
1175 }
1176
1177 return DeletionKind::Delete;
1178}
1179
1180bool LLVM::MemsetOp::loadsFrom(const MemorySlot &slot) { return false; }
1181
1182bool LLVM::MemsetOp::storesTo(const MemorySlot &slot) {
1183 return getDst() == slot.ptr;
1184}
1185
1186Value LLVM::MemsetOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1187 Value reachingDef,
1188 const DataLayout &dataLayout) {
1189 return memsetGetStored(*this, slot, builder);
1190}
1191
1192bool LLVM::MemsetOp::canUsesBeRemoved(
1193 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1194 SmallVectorImpl<OpOperand *> &newBlockingUses,
1195 const DataLayout &dataLayout) {
1196 return memsetCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1197 dataLayout);
1198}
1199
1200DeletionKind LLVM::MemsetOp::removeBlockingUses(
1201 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1202 OpBuilder &builder, Value reachingDefinition,
1203 const DataLayout &dataLayout) {
1204 return DeletionKind::Delete;
1205}
1206
1207LogicalResult LLVM::MemsetOp::ensureOnlySafeAccesses(
1208 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1209 const DataLayout &dataLayout) {
1210 return success(definitelyWritesOnlyWithinSlot(*this, slot, dataLayout));
1211}
1212
1213bool LLVM::MemsetOp::canRewire(const DestructurableMemorySlot &slot,
1214 SmallPtrSetImpl<Attribute> &usedIndices,
1215 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1216 const DataLayout &dataLayout) {
1217 return memsetCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1218 dataLayout);
1219}
1220
1221DeletionKind LLVM::MemsetOp::rewire(const DestructurableMemorySlot &slot,
1223 OpBuilder &builder,
1224 const DataLayout &dataLayout) {
1225 return memsetRewire(*this, slot, subslots, builder, dataLayout);
1226}
1227
1228bool LLVM::MemsetInlineOp::loadsFrom(const MemorySlot &slot) { return false; }
1229
1230bool LLVM::MemsetInlineOp::storesTo(const MemorySlot &slot) {
1231 return getDst() == slot.ptr;
1232}
1233
1234Value LLVM::MemsetInlineOp::getStored(const MemorySlot &slot,
1235 OpBuilder &builder, Value reachingDef,
1236 const DataLayout &dataLayout) {
1237 return memsetGetStored(*this, slot, builder);
1238}
1239
1240bool LLVM::MemsetInlineOp::canUsesBeRemoved(
1241 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1242 SmallVectorImpl<OpOperand *> &newBlockingUses,
1243 const DataLayout &dataLayout) {
1244 return memsetCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1245 dataLayout);
1246}
1247
1248DeletionKind LLVM::MemsetInlineOp::removeBlockingUses(
1249 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1250 OpBuilder &builder, Value reachingDefinition,
1251 const DataLayout &dataLayout) {
1252 return DeletionKind::Delete;
1253}
1254
1255LogicalResult LLVM::MemsetInlineOp::ensureOnlySafeAccesses(
1256 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1257 const DataLayout &dataLayout) {
1258 return success(definitelyWritesOnlyWithinSlot(*this, slot, dataLayout));
1259}
1260
1261bool LLVM::MemsetInlineOp::canRewire(
1262 const DestructurableMemorySlot &slot,
1263 SmallPtrSetImpl<Attribute> &usedIndices,
1264 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1265 const DataLayout &dataLayout) {
1266 return memsetCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1267 dataLayout);
1268}
1269
1271LLVM::MemsetInlineOp::rewire(const DestructurableMemorySlot &slot,
1273 OpBuilder &builder, const DataLayout &dataLayout) {
1274 return memsetRewire(*this, slot, subslots, builder, dataLayout);
1275}
1276
1277//===----------------------------------------------------------------------===//
1278// Interfaces for memcpy/memmove
1279//===----------------------------------------------------------------------===//
1280
1281template <class MemcpyLike>
1282static bool memcpyLoadsFrom(MemcpyLike op, const MemorySlot &slot) {
1283 return op.getSrc() == slot.ptr;
1284}
1285
1286template <class MemcpyLike>
1287static bool memcpyStoresTo(MemcpyLike op, const MemorySlot &slot) {
1288 return op.getDst() == slot.ptr;
1289}
1290
1291template <class MemcpyLike>
1292static Value memcpyGetStored(MemcpyLike op, const MemorySlot &slot,
1293 OpBuilder &builder) {
1294 return LLVM::LoadOp::create(builder, op.getLoc(), slot.elemType, op.getSrc());
1295}
1296
1297template <class MemcpyLike>
1298static bool
1299memcpyCanUsesBeRemoved(MemcpyLike op, const MemorySlot &slot,
1300 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1301 SmallVectorImpl<OpOperand *> &newBlockingUses,
1302 const DataLayout &dataLayout) {
1303 // If source and destination are the same, memcpy behavior is undefined and
1304 // memmove is a no-op. Because there is no memory change happening here,
1305 // simplifying such operations is left to canonicalization.
1306 if (op.getDst() == op.getSrc())
1307 return false;
1308
1309 if (op.getIsVolatile())
1310 return false;
1311
1312 return getStaticMemIntrLen(op) == dataLayout.getTypeSize(slot.elemType);
1313}
1314
1315template <class MemcpyLike>
1316static DeletionKind
1317memcpyRemoveBlockingUses(MemcpyLike op, const MemorySlot &slot,
1318 const SmallPtrSetImpl<OpOperand *> &blockingUses,
1319 OpBuilder &builder, Value reachingDefinition) {
1320 if (op.loadsFrom(slot))
1321 LLVM::StoreOp::create(builder, op.getLoc(), reachingDefinition,
1322 op.getDst());
1323 return DeletionKind::Delete;
1324}
1325
1326template <class MemcpyLike>
1327static LogicalResult
1328memcpyEnsureOnlySafeAccesses(MemcpyLike op, const MemorySlot &slot,
1329 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed) {
1330 DataLayout dataLayout = DataLayout::closest(op);
1331 // While rewiring memcpy-like intrinsics only supports full copies, partial
1332 // copies are still safe accesses so it is enough to only check for writes
1333 // within bounds.
1334 return success(definitelyWritesOnlyWithinSlot(op, slot, dataLayout));
1335}
1336
1337template <class MemcpyLike>
1338static bool memcpyCanRewire(MemcpyLike op, const DestructurableMemorySlot &slot,
1339 SmallPtrSetImpl<Attribute> &usedIndices,
1340 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1341 const DataLayout &dataLayout) {
1342 if (op.getIsVolatile())
1343 return false;
1344
1345 if (!cast<DestructurableTypeInterface>(slot.elemType).getSubelementIndexMap())
1346 return false;
1347
1348 if (!areAllIndicesI32(slot))
1349 return false;
1350
1351 // Only full copies are supported.
1352 if (getStaticMemIntrLen(op) != dataLayout.getTypeSize(slot.elemType))
1353 return false;
1354
1355 if (op.getSrc() == slot.ptr)
1356 usedIndices.insert_range(llvm::make_first_range(slot.subelementTypes));
1357
1358 return true;
1359}
1360
1361namespace {
1362
1363template <class MemcpyLike>
1364void createMemcpyLikeToReplace(OpBuilder &builder, const DataLayout &layout,
1365 MemcpyLike toReplace, Value dst, Value src,
1366 Type toCpy, bool isVolatile) {
1367 Value memcpySize =
1368 LLVM::ConstantOp::create(builder, toReplace.getLoc(),
1369 IntegerAttr::get(toReplace.getLen().getType(),
1370 layout.getTypeSize(toCpy)));
1371 MemcpyLike::create(builder, toReplace.getLoc(), dst, src, memcpySize,
1372 isVolatile);
1373}
1374
1375template <>
1376void createMemcpyLikeToReplace(OpBuilder &builder, const DataLayout &layout,
1377 LLVM::MemcpyInlineOp toReplace, Value dst,
1378 Value src, Type toCpy, bool isVolatile) {
1379 Type lenType = IntegerType::get(toReplace->getContext(),
1380 toReplace.getLen().getBitWidth());
1381 LLVM::MemcpyInlineOp::create(
1382 builder, toReplace.getLoc(), dst, src,
1383 IntegerAttr::get(lenType, layout.getTypeSize(toCpy)), isVolatile);
1384}
1385
1386} // namespace
1387
1388/// Rewires a memcpy-like operation. Only copies to or from the full slot are
1389/// supported.
1390template <class MemcpyLike>
1391static DeletionKind
1392memcpyRewire(MemcpyLike op, const DestructurableMemorySlot &slot,
1393 DenseMap<Attribute, MemorySlot> &subslots, OpBuilder &builder,
1394 const DataLayout &dataLayout) {
1395 if (subslots.empty())
1396 return DeletionKind::Delete;
1397
1398 assert((slot.ptr == op.getDst()) != (slot.ptr == op.getSrc()));
1399 bool isDst = slot.ptr == op.getDst();
1400
1401#ifndef NDEBUG
1402 size_t slotsTreated = 0;
1403#endif
1404
1405 // It was previously checked that index types are consistent, so this type can
1406 // be fetched now.
1407 Type indexType = cast<IntegerAttr>(subslots.begin()->first).getType();
1408 for (size_t i = 0, e = slot.subelementTypes.size(); i != e; i++) {
1409 Attribute index = IntegerAttr::get(indexType, i);
1410 if (!subslots.contains(index))
1411 continue;
1412 const MemorySlot &subslot = subslots.at(index);
1413
1414#ifndef NDEBUG
1415 slotsTreated++;
1416#endif
1417
1418 // First get a pointer to the equivalent of this subslot from the source
1419 // pointer.
1420 SmallVector<LLVM::GEPArg> gepIndices{
1421 0, static_cast<int32_t>(
1422 cast<IntegerAttr>(index).getValue().getZExtValue())};
1423 Value subslotPtrInOther = LLVM::GEPOp::create(
1424 builder, op.getLoc(), LLVM::LLVMPointerType::get(op.getContext()),
1425 slot.elemType, isDst ? op.getSrc() : op.getDst(), gepIndices);
1426
1427 // Then create a new memcpy out of this source pointer.
1428 createMemcpyLikeToReplace(builder, dataLayout, op,
1429 isDst ? subslot.ptr : subslotPtrInOther,
1430 isDst ? subslotPtrInOther : subslot.ptr,
1431 subslot.elemType, op.getIsVolatile());
1432 }
1433
1434 assert(subslots.size() == slotsTreated);
1435
1436 return DeletionKind::Delete;
1437}
1438
1439bool LLVM::MemcpyOp::loadsFrom(const MemorySlot &slot) {
1440 return memcpyLoadsFrom(*this, slot);
1441}
1442
1443bool LLVM::MemcpyOp::storesTo(const MemorySlot &slot) {
1444 return memcpyStoresTo(*this, slot);
1445}
1446
1447Value LLVM::MemcpyOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1448 Value reachingDef,
1449 const DataLayout &dataLayout) {
1450 return memcpyGetStored(*this, slot, builder);
1451}
1452
1453bool LLVM::MemcpyOp::canUsesBeRemoved(
1454 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1455 SmallVectorImpl<OpOperand *> &newBlockingUses,
1456 const DataLayout &dataLayout) {
1457 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1458 dataLayout);
1459}
1460
1461DeletionKind LLVM::MemcpyOp::removeBlockingUses(
1462 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1463 OpBuilder &builder, Value reachingDefinition,
1464 const DataLayout &dataLayout) {
1465 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1466 reachingDefinition);
1467}
1468
1469LogicalResult LLVM::MemcpyOp::ensureOnlySafeAccesses(
1470 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1471 const DataLayout &dataLayout) {
1472 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1473}
1474
1475bool LLVM::MemcpyOp::canRewire(const DestructurableMemorySlot &slot,
1476 SmallPtrSetImpl<Attribute> &usedIndices,
1477 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1478 const DataLayout &dataLayout) {
1479 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1480 dataLayout);
1481}
1482
1483DeletionKind LLVM::MemcpyOp::rewire(const DestructurableMemorySlot &slot,
1485 OpBuilder &builder,
1486 const DataLayout &dataLayout) {
1487 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1488}
1489
1490bool LLVM::MemcpyInlineOp::loadsFrom(const MemorySlot &slot) {
1491 return memcpyLoadsFrom(*this, slot);
1492}
1493
1494bool LLVM::MemcpyInlineOp::storesTo(const MemorySlot &slot) {
1495 return memcpyStoresTo(*this, slot);
1496}
1497
1498Value LLVM::MemcpyInlineOp::getStored(const MemorySlot &slot,
1499 OpBuilder &builder, Value reachingDef,
1500 const DataLayout &dataLayout) {
1501 return memcpyGetStored(*this, slot, builder);
1502}
1503
1504bool LLVM::MemcpyInlineOp::canUsesBeRemoved(
1505 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1506 SmallVectorImpl<OpOperand *> &newBlockingUses,
1507 const DataLayout &dataLayout) {
1508 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1509 dataLayout);
1510}
1511
1512DeletionKind LLVM::MemcpyInlineOp::removeBlockingUses(
1513 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1514 OpBuilder &builder, Value reachingDefinition,
1515 const DataLayout &dataLayout) {
1516 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1517 reachingDefinition);
1518}
1519
1520LogicalResult LLVM::MemcpyInlineOp::ensureOnlySafeAccesses(
1521 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1522 const DataLayout &dataLayout) {
1523 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1524}
1525
1526bool LLVM::MemcpyInlineOp::canRewire(
1527 const DestructurableMemorySlot &slot,
1528 SmallPtrSetImpl<Attribute> &usedIndices,
1529 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1530 const DataLayout &dataLayout) {
1531 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1532 dataLayout);
1533}
1534
1536LLVM::MemcpyInlineOp::rewire(const DestructurableMemorySlot &slot,
1538 OpBuilder &builder, const DataLayout &dataLayout) {
1539 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1540}
1541
1542bool LLVM::MemmoveOp::loadsFrom(const MemorySlot &slot) {
1543 return memcpyLoadsFrom(*this, slot);
1544}
1545
1546bool LLVM::MemmoveOp::storesTo(const MemorySlot &slot) {
1547 return memcpyStoresTo(*this, slot);
1548}
1549
1550Value LLVM::MemmoveOp::getStored(const MemorySlot &slot, OpBuilder &builder,
1551 Value reachingDef,
1552 const DataLayout &dataLayout) {
1553 return memcpyGetStored(*this, slot, builder);
1554}
1555
1556bool LLVM::MemmoveOp::canUsesBeRemoved(
1557 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1558 SmallVectorImpl<OpOperand *> &newBlockingUses,
1559 const DataLayout &dataLayout) {
1560 return memcpyCanUsesBeRemoved(*this, slot, blockingUses, newBlockingUses,
1561 dataLayout);
1562}
1563
1564DeletionKind LLVM::MemmoveOp::removeBlockingUses(
1565 const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
1566 OpBuilder &builder, Value reachingDefinition,
1567 const DataLayout &dataLayout) {
1568 return memcpyRemoveBlockingUses(*this, slot, blockingUses, builder,
1569 reachingDefinition);
1570}
1571
1572LogicalResult LLVM::MemmoveOp::ensureOnlySafeAccesses(
1573 const MemorySlot &slot, SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1574 const DataLayout &dataLayout) {
1575 return memcpyEnsureOnlySafeAccesses(*this, slot, mustBeSafelyUsed);
1576}
1577
1578bool LLVM::MemmoveOp::canRewire(const DestructurableMemorySlot &slot,
1579 SmallPtrSetImpl<Attribute> &usedIndices,
1580 SmallVectorImpl<MemorySlot> &mustBeSafelyUsed,
1581 const DataLayout &dataLayout) {
1582 return memcpyCanRewire(*this, slot, usedIndices, mustBeSafelyUsed,
1583 dataLayout);
1584}
1585
1586DeletionKind LLVM::MemmoveOp::rewire(const DestructurableMemorySlot &slot,
1588 OpBuilder &builder,
1589 const DataLayout &dataLayout) {
1590 return memcpyRewire(*this, slot, subslots, builder, dataLayout);
1591}
1592
1593//===----------------------------------------------------------------------===//
1594// Interfaces for destructurable types
1595//===----------------------------------------------------------------------===//
1596
1597std::optional<DenseMap<Attribute, Type>>
1598LLVM::LLVMStructType::getSubelementIndexMap() const {
1599 // Empty structs have no sub-elements and cannot be destructured.
1600 if (getBody().empty())
1601 return std::nullopt;
1602 Type i32 = IntegerType::get(getContext(), 32);
1603 DenseMap<Attribute, Type> destructured;
1604 for (const auto &[index, elemType] : llvm::enumerate(getBody()))
1605 destructured.insert({IntegerAttr::get(i32, index), elemType});
1606 return destructured;
1607}
1608
1609Type LLVM::LLVMStructType::getTypeAtIndex(Attribute index) const {
1610 auto indexAttr = llvm::dyn_cast<IntegerAttr>(index);
1611 if (!indexAttr || !indexAttr.getType().isInteger(32))
1612 return {};
1613 int32_t indexInt = indexAttr.getInt();
1614 ArrayRef<Type> body = getBody();
1615 if (indexInt < 0 || body.size() <= static_cast<uint32_t>(indexInt))
1616 return {};
1617 return body[indexInt];
1618}
1619
1620std::optional<DenseMap<Attribute, Type>>
1621LLVM::LLVMArrayType::getSubelementIndexMap() const {
1622 constexpr size_t maxArraySizeForDestructuring = 16;
1623 if (getNumElements() > maxArraySizeForDestructuring)
1624 return {};
1625 int32_t numElements = getNumElements();
1626
1627 Type i32 = IntegerType::get(getContext(), 32);
1628 DenseMap<Attribute, Type> destructured;
1629 for (int32_t index = 0; index < numElements; ++index)
1630 destructured.insert({IntegerAttr::get(i32, index), getElementType()});
1631 return destructured;
1632}
1633
1634Type LLVM::LLVMArrayType::getTypeAtIndex(Attribute index) const {
1635 auto indexAttr = llvm::dyn_cast<IntegerAttr>(index);
1636 if (!indexAttr || !indexAttr.getType().isInteger(32))
1637 return {};
1638 int32_t indexInt = indexAttr.getInt();
1639 if (indexInt < 0 || getNumElements() <= static_cast<uint32_t>(indexInt))
1640 return {};
1641 return getElementType();
1642}
return success()
static Value getBase(Value v)
Looks through known "view-like" ops to find the base memref.
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`
static Type getElementType(Type type, ArrayRef< int32_t > indices, function_ref< InFlightDiagnostic(StringRef)> emitErrorFn)
Walks the given type hierarchy with the given indices, potentially down to component granularity,...
Definition SPIRVOps.cpp:229
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:528
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:49
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:311
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.