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