114#include <mlir/Dialect/SCF/IR/SCF.h>
115#include <mlir/Dialect/SCF/Transforms/Patterns.h>
116#include <mlir/Dialect/UB/IR/UBOps.h>
117#include <mlir/Pass/PassManager.h>
118#include <mlir/Transforms/DialectConversion.h>
119#include <mlir/Transforms/GreedyPatternRewriteDriver.h>
120#include <mlir/Transforms/Passes.h>
122#include <llvm/ADT/DenseMapInfo.h>
123#include <llvm/ADT/DenseSet.h>
124#include <llvm/ADT/STLExtras.h>
125#include <llvm/ADT/TypeSwitch.h>
126#include <llvm/Support/Debug.h>
127#include <llvm/Support/raw_ostream.h>
135#define GEN_PASS_DEF_PODTOSCALARPASS
147#define DEBUG_TYPE "llzk-pod-to-scalar"
153 SmallVector<StringAttr> nameList;
154 bool syntheticShapeCarrier =
false;
156 RecordChain() =
default;
158 explicit RecordChain(ArrayRef<StringAttr>
names,
bool syntheticShape =
false)
159 : nameList(
names.begin(),
names.end()), syntheticShapeCarrier(syntheticShape) {}
162 StringRef getComponentName(
size_t componentIndex)
const {
163 assert(componentIndex < nameList.size() &&
"component index must be in range");
164 bool syntheticComponent = syntheticShapeCarrier && componentIndex + 1 == nameList.size();
165 return syntheticComponent ? StringRef(
"shape") : nameList[componentIndex].getValue();
169 RecordChain withPrefix(ArrayRef<StringAttr> prefix)
const {
170 SmallVector<StringAttr> fullChain(prefix.begin(), prefix.end());
171 llvm::append_range(fullChain, nameList);
172 return RecordChain(fullChain, syntheticShapeCarrier);
176 StringAttr getFlattenedMemberName(MLIRContext *ctx, StringAttr memberName)
const {
177 std::string flatName;
178 llvm::raw_string_ostream os(flatName);
179 os << memberName.getValue();
180 for (
size_t i = 0; i < nameList.size(); ++i) {
181 os <<
'_' << getComponentName(i);
183 return StringAttr::get(ctx, flatName);
186 bool operator==(
const RecordChain &other)
const {
187 return syntheticShapeCarrier == other.syntheticShapeCarrier && nameList == other.nameList;
192struct CompatiblePodLeafMaterializationKey {
196 bool operator==(
const CompatiblePodLeafMaterializationKey &other)
const {
197 return source == other.source && podType == other.podType;
201using CompatiblePodLeafMaterializationMap =
202 DenseMap<CompatiblePodLeafMaterializationKey, SmallVector<Value>>;
208template <>
struct DenseMapInfo<RecordChain> {
210 return llvm::hash_combine(
211 llvm::hash_combine_range(chain.nameList.begin(), chain.nameList.end()),
212 chain.syntheticShapeCarrier
216 static bool isEqual(
const RecordChain &lhs,
const RecordChain &rhs) {
return lhs == rhs; }
219template <>
struct DenseMapInfo<CompatiblePodLeafMaterializationKey> {
220 static unsigned getHashValue(
const CompatiblePodLeafMaterializationKey &key) {
221 return llvm::hash_combine(key.source, key.podType);
225 const CompatiblePodLeafMaterializationKey &lhs,
const CompatiblePodLeafMaterializationKey &rhs
236inline static bool isSamePodRecord(
ReadPodOp readOp, Value podRef, StringAttr recordName) {
241inline static bool isSamePodRecord(
WritePodOp writeOp, Value podRef, StringAttr recordName) {
246static bool hasNestedWriteToRecord(Operation &op, Value podRef, StringAttr recordName) {
247 return walkContains<WritePodOp>(op, [&](
WritePodOp writeOp) {
248 return writeOp.getOperation() != &op && isSamePodRecord(writeOp, podRef, recordName);
253static bool hasNestedWriteToPod(Operation &op, Value podRef) {
254 return walkContains<WritePodOp>(op, [&](
WritePodOp writeOp) {
255 return writeOp.getOperation() != &op && writeOp.
getPodRef() == podRef;
260static bool hasReadFromRecord(Operation &op, Value podRef, StringAttr recordName) {
261 return walkContains<ReadPodOp>(op, [&podRef, &recordName](
ReadPodOp readOp) {
262 return isSamePodRecord(readOp, podRef, recordName);
267static bool hasValueUse(Operation &op, Value value) {
268 return walkContains<Operation *>(op, [&value](Operation *nestedOp) {
269 return llvm::is_contained(nestedOp->getOperands(), value);
279findNearestForwardableWriteBefore(Operation *cursor, Value podRef, StringAttr recordName) {
280 for (Operation *op = cursor; op; op = op->getPrevNode()) {
281 if (!hasValueUse(*op, podRef)) {
286 if (isa<ReadPodOp>(op)) {
289 auto writeOp = dyn_cast<WritePodOp>(op);
290 return writeOp && isSamePodRecord(writeOp, podRef, recordName) ? writeOp :
nullptr;
297 return findNearestForwardableWriteBefore(
313 for (Operation *cursor = readOp.getOperation(); cursor && cursor->getBlock();) {
315 findNearestForwardableWriteBefore(cursor->getPrevNode(), podRef, recordName)) {
319 Operation *parentOp = cursor->getBlock()->getParentOp();
320 if (parentOp == loopBoundary) {
333inline static PodType splittablePod(Type t) {
334 if (
PodType pt = dyn_cast<PodType>(t)) {
335 return splittablePod(pt);
343inline static bool containsSplittablePodType(ArrayRef<Type> types) {
344 for (Type t : types) {
345 if (splittablePod(t)) {
354template <
typename T>
inline static bool containsSplittablePodType(ValueTypeRange<T> types) {
355 for (Type t : types) {
356 if (splittablePod(t)) {
369inline static ArrayType splittablePodArray(Type t) {
370 if (
ArrayType at = dyn_cast<ArrayType>(t)) {
371 return splittablePodArray(at);
377inline static bool containsSplittablePodArrayType(ArrayRef<Type> types) {
378 return llvm::any_of(types, [](Type t) {
return splittablePodArray(t); });
382template <
typename T>
inline static bool containsSplittablePodArrayType(ValueTypeRange<T> types) {
383 return llvm::any_of(types, [](Type t) {
return splittablePodArray(t); });
387inline static StringAttr getPodArrayShapeCarrierMarker(MLIRContext *ctx) {
388 return StringAttr::get(ctx,
"__llzk_pod_array_shape");
392[[maybe_unused]]
inline static bool isPodArrayShapeCarrierMarker(StringAttr recordName) {
393 return recordName && recordName == getPodArrayShapeCarrierMarker(recordName.getContext());
397inline constexpr llvm::StringLiteral DEFERRED_POD_ARRAY_LENGTH_ATTR =
398 "llzk.deferred_pod_array_length";
401inline constexpr llvm::StringLiteral RAGGED_DYNAMIC_NESTED_LEAF_ATTR =
402 "llzk.ragged_nested_dynamic_leaf";
405inline constexpr llvm::StringLiteral RAGGED_AFFINE_NESTED_LEAF_ATTR =
406 "llzk.ragged_nested_affine_leaf";
410 return arrTy.
cloneWith(NoneType::get(arrTy.getContext()));
414inline static bool hasWildcardArrayDimensions(ArrayRef<Attribute> dims) {
415 return llvm::any_of(dims, [](Attribute dim) {
416 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(dim)) {
424inline static bool hasAffineArrayDimensions(ArrayRef<Attribute> dims) {
425 return llvm::any_of(dims, [](Attribute dim) {
return llvm::isa<AffineMapAttr>(dim); });
429template <
typename Fn>
430static void forEachPodLeaf(
PodType podTy, SmallVectorImpl<StringAttr> &recordChain, Fn &&callback);
433static size_t splitPodArrayTypeTo(
434 Type t, SmallVectorImpl<Type> &collect, SmallVector<RecordChain> *splitIds =
nullptr
436 if (
ArrayType at = splittablePodArray(t)) {
437 auto podTy = llvm::cast<PodType>(at.getElementType());
438 SmallVector<StringAttr> recordChain;
439 size_t originalSize = collect.size();
440 forEachPodLeaf(podTy, recordChain, [&](RecordChain
id, Type leafType) {
443 splitIds->push_back(std::move(
id));
446 return collect.size() - originalSize;
449 collect.push_back(t);
454static bool hasZeroLeafPodArraySplit(
ArrayType arrTy) {
455 SmallVector<Type> splitTypes;
456 splitPodArrayTypeTo(arrTy, splitTypes);
457 return splitTypes.empty();
464static size_t countRankPreservingSplitPodArrayLeaves(
ArrayType arrTy) {
465 SmallVector<Type> splitTypes;
466 splitPodArrayTypeTo(arrTy, splitTypes);
468 return llvm::count_if(splitTypes, [originalRank](Type splitType) {
469 auto splitArrTy = llvm::dyn_cast<ArrayType>(splitType);
470 return splitArrTy && splitArrTy.getDimensionSizes().size() == originalRank;
480static bool needsPodArrayShapeCarrier(
ArrayType arrTy) {
481 if (hasZeroLeafPodArraySplit(arrTy)) {
488 return countRankPreservingSplitPodArrayLeaves(arrTy) != 1;
492static void collectConvertedPodArrayRecordInfos(
493 ArrayType arrTy, SmallVector<RecordChain> &splitIds, SmallVectorImpl<Type> &splitTypes
495 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
496 if (needsPodArrayShapeCarrier(arrTy)) {
497 splitIds.push_back(RecordChain({getPodArrayShapeCarrierMarker(arrTy.getContext())},
true));
498 splitTypes.push_back(getPodArrayShapeCarrierType(arrTy));
503static StringRef getRaggedNestedLeafAttrName(
ArrayType arrTy, Type splitType) {
504 auto splitArrTy = llvm::dyn_cast<ArrayType>(splitType);
510 if (splitArrTy.getDimensionSizes().size() <= originalRank) {
514 ArrayRef<Attribute> nestedDims = splitArrTy.getDimensionSizes().drop_front(originalRank);
515 if (hasWildcardArrayDimensions(nestedDims)) {
516 return RAGGED_DYNAMIC_NESTED_LEAF_ATTR;
518 if (hasAffineArrayDimensions(nestedDims)) {
519 return RAGGED_AFFINE_NESTED_LEAF_ATTR;
525static StringRef getRaggedNestedLeafKind(
ArrayType arrTy, Type splitType) {
526 StringRef attrName = getRaggedNestedLeafAttrName(arrTy, splitType);
527 if (attrName == RAGGED_DYNAMIC_NESTED_LEAF_ATTR) {
530 if (attrName == RAGGED_AFFINE_NESTED_LEAF_ATTR) {
537static StringRef getRaggedNestedLeafKind(
ArrayType arrTy) {
538 SmallVector<Type> splitTypes;
539 splitPodArrayTypeTo(arrTy, splitTypes);
540 for (Type splitType : splitTypes) {
541 if (StringRef raggedKind = getRaggedNestedLeafKind(arrTy, splitType); !raggedKind.empty()) {
550tagRaggedNestedLeafValue(OpBuilder &bldr, Location loc, Value value, StringRef attrName) {
551 if (attrName.empty()) {
554 auto cast = UnrealizedConversionCastOp::create(bldr, loc, TypeRange {value.getType()}, value);
555 cast->setAttr(attrName, UnitAttr::get(bldr.getContext()));
556 return cast.getResult(0);
560static StringRef getTaggedRaggedNestedLeafKind(Value value) {
563 value =
cast.getInput();
566 auto cast = value.getDefiningOp<UnrealizedConversionCastOp>();
570 if (
cast->hasAttr(RAGGED_DYNAMIC_NESTED_LEAF_ATTR)) {
573 if (
cast->hasAttr(RAGGED_AFFINE_NESTED_LEAF_ATTR)) {
576 if (
cast->getNumOperands() != 1) {
579 value =
cast.getOperand(0);
587 StringRef raggedKind = getTaggedRaggedNestedLeafKind(lhsLeaf);
588 if (raggedKind.empty()) {
589 raggedKind = getTaggedRaggedNestedLeafKind(rhsLeaf);
591 if (raggedKind.empty()) {
594 return op.emitOpError() <<
"cannot lower nested " << raggedKind
595 <<
" array leaf equality after reading an array-of-POD element without "
596 "per-element shape witnesses";
601 StringRef raggedKind = getTaggedRaggedNestedLeafKind(op.
getLhs());
602 if (raggedKind.empty()) {
603 raggedKind = getTaggedRaggedNestedLeafKind(op.
getRhs());
605 if (raggedKind.empty()) {
608 return op.emitOpError() <<
"cannot lower nested " << raggedKind
609 <<
" array leaf containment after reading an array-of-POD element "
610 "without per-element shape witnesses";
614template <
typename ValueRangeLike>
616rejectRaggedNestedLeafBoundaryCrossing(Operation *op, ValueRangeLike values, StringRef boundary) {
617 StringRef raggedKind;
618 for (Value value : values) {
619 raggedKind = getTaggedRaggedNestedLeafKind(value);
620 if (!raggedKind.empty()) {
624 if (raggedKind.empty()) {
627 return op->emitOpError() <<
"cannot pass nested " << raggedKind <<
" array leaf " << boundary
628 <<
" after reading an array-of-POD element without per-element shape "
633static Value peelUnifiableCasts(Value value) {
635 value =
cast.getInput();
641static Type getFlattenedTypeAlongPath(
642 Type type, ArrayRef<StringAttr> recordChain,
bool syntheticShapeCarrier =
false
644 if (recordChain.empty()) {
648 if (
PodType podTy = dyn_cast<PodType>(type)) {
649 Type nextType = podTy.getRecordMap().lookup(recordChain.front().getValue());
650 assert(nextType &&
"record path must exist in the containing POD");
651 return getFlattenedTypeAlongPath(nextType, recordChain.drop_front(), syntheticShapeCarrier);
654 if (
ArrayType arrTy = splittablePodArray(type)) {
655 if (syntheticShapeCarrier && recordChain.size() == 1) {
657 isPodArrayShapeCarrierMarker(recordChain.front()) &&
658 "synthetic shape carrier must use the reserved shape marker"
660 assert(needsPodArrayShapeCarrier(arrTy) &&
"shape marker requires an explicit array carrier");
661 return getPodArrayShapeCarrierType(arrTy);
665 Type nextType = elemPodTy.getRecordMap().lookup(recordChain.front().getValue());
666 assert(nextType &&
"record path must exist in the POD array element type");
668 arrTy, getFlattenedTypeAlongPath(nextType, recordChain.drop_front(), syntheticShapeCarrier)
672 llvm_unreachable(
"record path cannot continue through a non-POD leaf");
676inline static Type getFlattenedTypeAlongPath(Type type,
const RecordChain &recordChain) {
677 return getFlattenedTypeAlongPath(
678 type, ArrayRef(recordChain.nameList), recordChain.syntheticShapeCarrier
683template <
typename Fn>
684static void forEachPodLeaf(
PodType podTy, SmallVectorImpl<StringAttr> &recordChain, Fn &&callback) {
685 std::function<void(Type)> walk = [&](Type type) {
686 if (
PodType nestedPodTy = llvm::dyn_cast<PodType>(type)) {
687 for (RecordAttr record : nestedPodTy.getRecords()) {
688 recordChain.push_back(record.getName());
689 walk(record.getType());
690 recordChain.pop_back();
692 }
else if (
ArrayType arrTy = splittablePodArray(type)) {
694 for (RecordAttr record : elemPodTy.getRecords()) {
695 recordChain.push_back(record.getName());
697 recordChain.pop_back();
699 if (needsPodArrayShapeCarrier(arrTy)) {
700 recordChain.push_back(getPodArrayShapeCarrierMarker(arrTy.getContext()));
701 callback(RecordChain(recordChain,
true), getPodArrayShapeCarrierType(arrTy));
702 recordChain.pop_back();
705 callback(RecordChain(recordChain), type);
714size_t splitPodTypeTo(Type t, SmallVector<Type> &collect) {
715 if (
PodType pt = splittablePod(t)) {
716 SmallVector<StringAttr> recordChain;
717 size_t originalSize = collect.size();
718 forEachPodLeaf(pt, recordChain, [&collect](
const RecordChain &, Type leafType) {
719 collect.push_back(leafType);
721 return collect.size() - originalSize;
723 collect.push_back(t);
729template <
typename TypeCollection>
730inline void splitPodTypeTo(
731 TypeCollection types, SmallVector<Type> &collect, SmallVector<size_t> *originalIdxToSize
733 for (Type t : types) {
734 size_t count = splitPodTypeTo(t, collect);
735 if (originalIdxToSize) {
736 originalIdxToSize->push_back(count);
743template <
typename TypeCollection>
744inline SmallVector<Type>
745splitPodType(TypeCollection types, SmallVector<size_t> *originalIdxToSize =
nullptr) {
746 SmallVector<Type> collect;
747 splitPodTypeTo(types, collect, originalIdxToSize);
753getConvertedPodArrayShapeCarrierIfPresent(
ArrayType arrTy, ValueRange convertedValues) {
754 SmallVector<Type> splitTypes;
755 splitPodArrayTypeTo(arrTy, splitTypes);
756 if (!needsPodArrayShapeCarrier(arrTy) || convertedValues.size() != splitTypes.size() + 1) {
759 return convertedValues.back();
768static size_t convertPodArrayTypeTo(Type t, SmallVectorImpl<Type> &collect) {
769 if (
ArrayType arrTy = splittablePodArray(t)) {
770 size_t oldSize = collect.size();
771 splitPodArrayTypeTo(arrTy, collect);
772 if (needsPodArrayShapeCarrier(arrTy)) {
773 collect.push_back(getPodArrayShapeCarrierType(arrTy));
775 return collect.size() - oldSize;
778 collect.push_back(t);
783template <
typename TypeCollection>
784inline void convertPodArrayTypesTo(
785 TypeCollection types, SmallVectorImpl<Type> &collect,
786 SmallVector<size_t> *originalIdxToSize =
nullptr
788 if (originalIdxToSize) {
789 originalIdxToSize->reserve(types.size());
791 for (Type t : types) {
792 size_t count = convertPodArrayTypeTo(t, collect);
793 if (originalIdxToSize) {
794 originalIdxToSize->push_back(count);
800template <
typename TypeCollection>
801inline static SmallVector<Type>
802convertPodArrayTypes(TypeCollection types, SmallVector<size_t> *originalIdxToSize =
nullptr) {
803 SmallVector<Type> collect;
804 convertPodArrayTypesTo(types, collect, originalIdxToSize);
809static SmallVector<std::string> getSplitPodArrayRecordNameSuffixes(Type type) {
810 SmallVector<std::string> suffixes;
811 if (
ArrayType at = splittablePodArray(type)) {
812 SmallVector<RecordChain> splitIds;
813 SmallVector<Type> ignoredTypes;
814 splitPodArrayTypeTo(at, ignoredTypes, &splitIds);
815 suffixes.reserve(splitIds.size());
816 for (
const RecordChain &
id : splitIds) {
818 llvm::raw_string_ostream os(suffix);
819 for (
size_t i = 0; i <
id.nameList.size(); ++i) {
820 os <<
'.' <<
id.getComponentName(i);
822 suffixes.push_back(std::move(suffix));
824 if (needsPodArrayShapeCarrier(at)) {
825 suffixes.push_back(
".shape");
836static Value castValueToTypeIfNeeded(OpBuilder &bldr, Location loc, Value value, Type targetType) {
837 if (value.getType() == targetType) {
840 assert(
typesUnify(value.getType(), targetType) &&
"expected compatible rewritten types");
846genRead(OpBuilder &bldr, Location loc, Value podRef, StringAttr recordName) {
848 llvm::cast<PodType>(podRef.getType()).getRecordMap().lookup(recordName.getValue());
854genWrite(OpBuilder &bldr, Location loc, Value podRef, StringAttr recordName, Value value) {
856 llvm::cast<PodType>(podRef.getType()).getRecordMap().lookup(recordName.getValue());
858 bldr, loc, podRef, recordName, castValueToTypeIfNeeded(bldr, loc, value, recordType)
863inline static Value getSingleConvertedValue(ValueRange values) {
864 assert(values.size() == 1 &&
"expected a 1:1 converted value range");
865 return values.front();
869inline static size_t getSplitPodArrayLeafCount(
ArrayType arrTy) {
870 SmallVector<Type> splitTypes;
871 splitPodArrayTypeTo(arrTy, splitTypes);
872 return splitTypes.size();
876static ValueRange getConvertedPodArrayLeafValues(
ArrayType arrTy, ValueRange convertedValues) {
877 size_t leafCount = getSplitPodArrayLeafCount(arrTy);
878 return convertedValues.take_front(
879 convertedValues.size() < leafCount ? convertedValues.size() : leafCount
884static bool hasEarlierWriteToRecordInBlock(Operation *op, Value podRef, StringAttr recordName) {
885 for (Operation &candidate : *op->getBlock()) {
886 if (&candidate == op) {
889 if (
auto writeOp = dyn_cast<WritePodOp>(&candidate)) {
890 if (isSamePodRecord(writeOp, podRef, recordName)) {
893 }
else if (hasNestedWriteToRecord(candidate, podRef, recordName)) {
902static bool hasEarlierWriteToRecord(Operation *op, Value podRef, StringAttr recordName) {
903 for (Operation *cursor = op; cursor && cursor->getBlock();) {
904 if (hasEarlierWriteToRecordInBlock(cursor, podRef, recordName)) {
907 cursor = cursor->getBlock()->getParentOp();
913inline static bool hasEarlierWriteInBlock(
ReadPodOp readOp) {
914 return hasEarlierWriteToRecordInBlock(
921inline static bool hasEarlierWrite(
ReadPodOp readOp) {
922 return hasEarlierWriteToRecord(
928static bool isFreshUnwrittenPodRead(
ReadPodOp readOp) {
933 auto isReadOpRecordName = [&readOp](Attribute attr) {
937 !hasEarlierWrite(readOp);
941static void collectNewPodMapAttrs(Type type, SmallVectorImpl<AffineMapAttr> &mapAttrs) {
942 llvm::TypeSwitch<Type, void>(type)
943 .Case([&mapAttrs](
PodType podTy) {
944 for (RecordAttr record : podTy.
getRecords()) {
945 collectNewPodMapAttrs(record.getType(), mapAttrs);
950 if (
auto mapAttr = llvm::dyn_cast<AffineMapAttr>(dimSize)) {
951 mapAttrs.push_back(mapAttr);
956 if (ArrayAttr params = structTy.
getParams()) {
957 for (Attribute param : params) {
958 if (
auto mapAttr = llvm::dyn_cast<AffineMapAttr>(param)) {
959 mapAttrs.push_back(mapAttr);
963 }).Default([](Type) {});
970struct ArrayInstantiationInfo {
971 SmallVector<SmallVector<Value>> mapOperandStorage;
972 SmallVector<int32_t> numDimsPerMap;
976static std::optional<ArrayInstantiationInfo>
977tryGetFreshUnwrittenPodReadInstantiationInfo(
ReadPodOp readOp) {
978 if (!isFreshUnwrittenPodRead(readOp)) {
987 size_t groupBegin = 0;
989 PodType newPodTy = newPod.getType();
990 for (RecordAttr record : newPodTy.
getRecords()) {
991 SmallVector<AffineMapAttr> recordMapAttrs;
992 collectNewPodMapAttrs(record.getType(), recordMapAttrs);
993 size_t recordGroupCount = recordMapAttrs.size();
995 if (recordGroupCount == 0 || newPod.
getMapOperands().size() < groupBegin + recordGroupCount) {
999 ArrayInstantiationInfo info;
1000 info.mapOperandStorage.reserve(recordGroupCount);
1001 info.numDimsPerMap.reserve(recordGroupCount);
1002 for (
size_t i = 0; i < recordGroupCount; ++i) {
1004 info.mapOperandStorage.emplace_back(group.begin(), group.end());
1006 groupBegin + i < allNumDims.size()
1007 ? allNumDims[groupBegin + i]
1009 info.numDimsPerMap.push_back(numDims);
1013 groupBegin += recordGroupCount;
1016 return std::nullopt;
1020static size_t countRecursiveAffineMapAttrs(Type type) {
1021 return llvm::TypeSwitch<Type, size_t>(type)
1024 for (RecordAttr record : podTy.
getRecords()) {
1025 count += countRecursiveAffineMapAttrs(record.getType());
1032 if (llvm::isa<AffineMapAttr>(dimSize)) {
1036 return count + countRecursiveAffineMapAttrs(arrTy.
getElementType());
1040 if (ArrayAttr params = structTy.
getParams()) {
1041 for (Attribute param : params) {
1042 if (llvm::isa<AffineMapAttr>(param)) {
1044 }
else if (
auto typeAttr = llvm::dyn_cast<TypeAttr>(param)) {
1045 count += countRecursiveAffineMapAttrs(typeAttr.getValue());
1050 }).Default([](Type) {
return 0; });
1058static std::optional<ArrayInstantiationInfo> tryGetArrayInstantiationInfo(Value value) {
1060 value =
cast.getInput();
1064 if (
WritePodOp write = findNearestForwardableWrite(read)) {
1065 return tryGetArrayInstantiationInfo(write.getValue());
1067 return tryGetFreshUnwrittenPodReadInstantiationInfo(read);
1072 return std::nullopt;
1075 ArrayInstantiationInfo info;
1076 info.mapOperandStorage.reserve(
create.getMapOperands().size());
1077 for (OperandRange group :
create.getMapOperands()) {
1078 info.mapOperandStorage.emplace_back(group.begin(), group.end());
1081 if (DenseI32ArrayAttr numDimsPerMap =
create.getNumDimsPerMapAttr()) {
1082 llvm::append_range(info.numDimsPerMap, numDimsPerMap.asArrayRef());
1092static Value materializeArrayLengthCarrier(
1093 Value originalArrRef,
ArrayType originalArrTy, Location loc, OpBuilder &rewriter
1095 ArrayType carrierTy = getPodArrayShapeCarrierType(originalArrTy);
1098 if (
create.getMapOperands().empty()) {
1102 SmallVector<ValueRange> mapOperands;
1103 mapOperands.reserve(
create.getMapOperands().size());
1104 for (OperandRange mapOperandGroup :
create.getMapOperands()) {
1105 mapOperands.push_back(mapOperandGroup);
1108 rewriter, loc, carrierTy, mapOperands,
create.getNumDimsPerMapAttr()
1112 if (std::optional<ArrayInstantiationInfo> instantiation =
1113 tryGetArrayInstantiationInfo(originalArrRef)) {
1114 if (instantiation->mapOperandStorage.empty()) {
1118 SmallVector<ValueRange> mapOperands;
1119 mapOperands.reserve(instantiation->mapOperandStorage.size());
1120 for (
const SmallVector<Value> &group : instantiation->mapOperandStorage) {
1121 mapOperands.push_back(group);
1124 rewriter, loc, carrierTy, mapOperands, ArrayRef<int32_t>(instantiation->numDimsPerMap)
1128 bool hasAffineDims = llvm::any_of(originalArrTy.
getDimensionSizes(), [](Attribute dimSize) {
1129 return llvm::isa<AffineMapAttr>(dimSize);
1131 if (!hasAffineDims) {
1143static Value materializeExtractedPodArrayShapeCarrier(
1145 ArrayRef<Value> indices, ConversionPatternRewriter &rewriter
1147 ArrayType originalArrTy = llvm::cast<ArrayType>(originalArrRef.getType());
1148 Value sourceCarrier = getConvertedPodArrayShapeCarrierIfPresent(originalArrTy, convertedArrRefs);
1149 if (!sourceCarrier) {
1151 materializeArrayLengthCarrier(originalArrRef, originalArrTy, op.getLoc(), rewriter);
1155 rewriter, op.getLoc(), getPodArrayShapeCarrierType(resultTy), sourceCarrier, indices
1160static Value getRankPreservingConvertedPodArrayLeaf(
ArrayType arrTy, ValueRange convertedValues) {
1162 for (Value arrRef : getConvertedPodArrayLeafValues(arrTy, convertedValues)) {
1163 auto leafArrTy = llvm::dyn_cast<ArrayType>(arrRef.getType());
1164 if (leafArrTy && leafArrTy.getDimensionSizes().size() == originalRank) {
1172static Value getConvertedPodArrayShapeSource(
ArrayType arrTy, ValueRange convertedValues) {
1173 if (Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, convertedValues)) {
1176 return getRankPreservingConvertedPodArrayLeaf(arrTy, convertedValues);
1180template <
typename RangeOfRanges>
1181inline static SmallVector<Value> flattenConvertedValues(RangeOfRanges ranges) {
1182 SmallVector<Value> values;
1183 for (ValueRange range : ranges) {
1184 llvm::append_range(values, range);
1190struct FlattenedConvertedValueRangeStorage {
1191 SmallVector<SmallVector<Value>> storage;
1192 SmallVector<ValueRange> ranges;
1194 template <
typename RangeOfRanges>
1195 explicit FlattenedConvertedValueRangeStorage(
const RangeOfRanges &valueRanges) {
1196 storage.reserve(valueRanges.size());
1197 ranges.reserve(valueRanges.size());
1198 for (ArrayRef<ValueRange> valueRangeGroup : valueRanges) {
1199 storage.push_back(flattenConvertedValues(valueRangeGroup));
1201 for (
const SmallVector<Value> &values : storage) {
1202 ranges.push_back(values);
1209template <
typename ValueRangeLike>
1210inline static bool allValueTypesUnifyWithTypes(
const ValueRangeLike &values, ArrayRef<Type> types) {
1211 return llvm::all_of_zip(values, types, [](
auto value, Type type) {
1221static ArrayType getSplitPodArrayStorageType(
ArrayType arrTy, ArrayRef<StringAttr> recordChain) {
1223 Type leafType = getFlattenedTypeAlongPath(elemPodTy, recordChain);
1228static Value createSplitPodArrayReplacement(
1229 Operation *src, Location loc,
ArrayType originalArrTy,
const RecordChain &
id,
1230 ArrayType preciseSplitType, ConversionPatternRewriter &rewriter,
1231 ArrayRef<ValueRange> mapOperands = {}, DenseI32ArrayAttr numDimsPerMap = nullptr
1233 ArrayType storageSplitType = getSplitPodArrayStorageType(originalArrTy,
id.nameList);
1239 return castValueToTypeIfNeeded(rewriter, loc, splitArrayOp, preciseSplitType);
1246inline static Value createWritableArrayValue(
1247 OpBuilder &bldr, Location loc,
ArrayType arrTy,
1248 std::optional<ArrayInstantiationInfo> instantiation = std::nullopt
1251 SmallVector<AffineMapAttr> topLevelMapAttrs;
1253 if (
auto mapAttr = llvm::dyn_cast<AffineMapAttr>(dimSize)) {
1254 topLevelMapAttrs.push_back(mapAttr);
1258 if (instantiation && !topLevelMapAttrs.empty() &&
1259 countRecursiveAffineMapAttrs(arrTy) == topLevelMapAttrs.size() &&
1260 instantiation->mapOperandStorage.size() == topLevelMapAttrs.size() &&
1261 instantiation->numDimsPerMap.size() == topLevelMapAttrs.size()) {
1262 SmallVector<ValueRange> mapOperands;
1263 mapOperands.reserve(instantiation->mapOperandStorage.size());
1264 for (
const SmallVector<Value> &group : instantiation->mapOperandStorage) {
1265 mapOperands.push_back(group);
1277static std::optional<Value>
1278tryMaterializeFreshUnwrittenDirectRecordRead(OpBuilder &bldr, Location loc,
ReadPodOp readOp) {
1279 if (!isFreshUnwrittenPodRead(readOp)) {
1280 return std::nullopt;
1283 Type recordType = readOp.getType();
1284 if (llvm::isa<PodType>(recordType)) {
1285 return std::nullopt;
1288 if (
ArrayType arrTy = llvm::dyn_cast<ArrayType>(recordType)) {
1289 return createWritableArrayValue(
1290 bldr, loc, arrTy, tryGetFreshUnwrittenPodReadInstantiationInfo(readOp)
1298static bool equivalentArrayInstantiationInfo(
1299 const ArrayInstantiationInfo &lhs,
const ArrayInstantiationInfo &rhs
1301 if (lhs.numDimsPerMap != rhs.numDimsPerMap ||
1302 lhs.mapOperandStorage.size() != rhs.mapOperandStorage.size()) {
1306 for (
auto [lhsGroup, rhsGroup] : llvm::zip_equal(lhs.mapOperandStorage, rhs.mapOperandStorage)) {
1307 if (lhsGroup != rhsGroup) {
1316enum class CommonArrayInstantiationStatus : std::uint8_t {
1327static CommonArrayInstantiationStatus
1328inferCommonArrayInstantiation(ArrayRef<Value> values, ArrayInstantiationInfo &result) {
1329 bool initialized =
false;
1330 for (Value value : values) {
1331 std::optional<ArrayInstantiationInfo> info = tryGetArrayInstantiationInfo(value);
1333 return CommonArrayInstantiationStatus::unavailable;
1337 result = std::move(*info);
1342 if (!equivalentArrayInstantiationInfo(result, *info)) {
1343 return CommonArrayInstantiationStatus::conflict;
1347 return initialized ? CommonArrayInstantiationStatus::inferred
1348 : CommonArrayInstantiationStatus::unavailable;
1353genArrayWrite(OpBuilder &bldr, Location loc, Value arrayRef, ValueRange indices, Value value) {
1354 ArrayType arrTy = llvm::cast<ArrayType>(arrayRef.getType());
1356 Value convertedValue = castValueToTypeIfNeeded(bldr, loc, value, selectedType);
1357 if (llvm::isa<ArrayType>(selectedType)) {
1363inline static Operation *
1364genArrayWrite(OpBuilder &bldr, Location loc, Value arrayRef, ArrayAttr index, Value value) {
1366 return genArrayWrite(bldr, loc, arrayRef, indices, value);
1370static bool tryCollectDirectConvertedPodArrayValues(
1371 Value arrayValue,
ArrayType arrTy, ArrayRef<Type> convertedTypes,
1372 SmallVectorImpl<Value> &convertedValues
1374 arrayValue = peelUnifiableCasts(arrayValue);
1376 if (
auto cast = arrayValue.getDefiningOp<UnrealizedConversionCastOp>()) {
1377 if (
cast->getNumResults() != 1 ||
cast.getResult(0).getType() != arrTy ||
1378 cast->getNumOperands() != convertedTypes.size()) {
1383 cast.getOperands(), convertedTypes, convertedValues
1391static Value tryCollectDirectConvertedPodArrayShapeSource(
1392 Value arrayValue,
ArrayType arrTy, Location loc, OpBuilder &bldr
1394 SmallVector<RecordChain> splitIds;
1395 SmallVector<Type> convertedTypes;
1396 collectConvertedPodArrayRecordInfos(arrTy, splitIds, convertedTypes);
1398 SmallVector<Value> convertedValues;
1399 if (!tryCollectDirectConvertedPodArrayValues(
1400 arrayValue, arrTy, convertedTypes, convertedValues
1405 if (Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, convertedValues)) {
1406 return castValueToTypeIfNeeded(bldr, loc, carrier, getPodArrayShapeCarrierType(arrTy));
1408 if (Value leaf = getRankPreservingConvertedPodArrayLeaf(arrTy, convertedValues)) {
1416static bool hasEarlierWriteToPodInBlock(Operation *op, Value podRef) {
1417 for (Operation &candidate : *op->getBlock()) {
1418 if (&candidate == op) {
1421 if (
auto writeOp = dyn_cast<WritePodOp>(&candidate)) {
1425 }
else if (hasNestedWriteToPod(candidate, podRef)) {
1433static bool hasEarlierWriteToPod(Operation *op, Value podRef) {
1434 for (Operation *cursor = op; cursor && cursor->getBlock();) {
1435 if (hasEarlierWriteToPodInBlock(cursor, podRef)) {
1438 cursor = cursor->getBlock()->getParentOp();
1444static bool isFreshUnwrittenPodArrayRead(Value value) {
1445 value = peelUnifiableCasts(value);
1447 return readOp && splittablePodArray(readOp.getType()) && isFreshUnwrittenPodRead(readOp);
1451static Value genReadAlongPath(
1452 OpBuilder &bldr, Location loc, Value value, ArrayRef<StringAttr> recordChain,
1453 bool syntheticShapeCarrier
1455 if (recordChain.empty()) {
1456 if (
ReadPodOp readOp = peelUnifiableCasts(value).getDefiningOp<ReadPodOp>()) {
1457 if (std::optional<Value> materialized =
1458 tryMaterializeFreshUnwrittenDirectRecordRead(bldr, loc, readOp)) {
1459 return *materialized;
1465 Type valueType = value.getType();
1466 if (llvm::isa<PodType>(valueType)) {
1467 Value nextValue = genRead(bldr, loc, value, recordChain.front());
1468 return genReadAlongPath(bldr, loc, nextValue, recordChain.drop_front(), syntheticShapeCarrier);
1471 if (
ArrayType arrTy = splittablePodArray(valueType)) {
1472 Type splitType = getFlattenedTypeAlongPath(valueType, recordChain, syntheticShapeCarrier);
1473 auto splitArrTy = llvm::dyn_cast<ArrayType>(splitType);
1476 SmallVector<RecordChain> splitIds;
1477 SmallVector<Type> splitTypes;
1478 collectConvertedPodArrayRecordInfos(arrTy, splitIds, splitTypes);
1479 auto *splitIt = llvm::find(splitIds, RecordChain(recordChain, syntheticShapeCarrier));
1480 assert(splitIt != splitIds.end() &&
"record path must name a flattened POD array leaf");
1481 size_t splitIdx = std::distance(splitIds.begin(), splitIt);
1483 SmallVector<Value> convertedValues;
1484 if (tryCollectDirectConvertedPodArrayValues(value, arrTy, splitTypes, convertedValues)) {
1485 return convertedValues[splitIdx];
1488 Value strippedValue = peelUnifiableCasts(value);
1489 if (isFreshUnwrittenPodArrayRead(value)) {
1490 return createWritableArrayValue(bldr, loc, splitArrTy, tryGetArrayInstantiationInfo(value));
1493 if (strippedValue.getDefiningOp<
ReadPodOp>()) {
1495 UnrealizedConversionCastOp::create(bldr, loc, TypeRange(splitTypes), strippedValue);
1496 return splitReads.getResult(splitIdx);
1499 if (syntheticShapeCarrier) {
1501 isPodArrayShapeCarrierMarker(recordChain.back()) &&
1502 "synthetic shape carrier must use the reserved shape marker"
1507 if (!arrTy.hasStaticShape()) {
1509 "non-static nested array-of-POD scalarization requires split-array backing or an "
1510 "uninitialized pod field"
1515 assert(subIndices &&
"static-shape arrays must provide subelement indices");
1517 Value splitArray = createWritableArrayValue(bldr, loc, splitArrTy);
1518 for (ArrayAttr index : *subIndices) {
1520 Value leafValue = genReadAlongPath(bldr, loc, element, recordChain, syntheticShapeCarrier);
1521 genArrayWrite(bldr, loc, splitArray, index, leafValue);
1526 llvm_unreachable(
"record path cannot continue through a non-POD leaf");
1531genReadAlongPath(OpBuilder &bldr, Location loc, Value podRef,
const RecordChain &recordChain) {
1532 return genReadAlongPath(
1533 bldr, loc, podRef, ArrayRef(recordChain.nameList), recordChain.syntheticShapeCarrier
1538static SmallVector<Value> materializeCompatiblePodArrayLeafValues(
1539 Location loc, Value arrayValue,
ArrayType arrTy, OpBuilder &rewriter
1541 SmallVector<RecordChain> splitIds;
1542 SmallVector<Type> splitTypes;
1543 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
1545 SmallVector<Value> leaves;
1546 leaves.reserve(splitIds.size());
1547 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
1548 Value splitValue = genReadAlongPath(rewriter, loc, arrayValue,
id);
1549 leaves.push_back(castValueToTypeIfNeeded(rewriter, loc, splitValue, splitType));
1554using VirtualPodLeafMap = DenseMap<RecordChain, Value>;
1555using VirtualPodValueMap = DenseMap<Value, VirtualPodLeafMap>;
1558static Value rebuildFlattenedPodRecord(
1559 OpBuilder &bldr, Location loc, Type recordType, SmallVectorImpl<StringAttr> &recordChain,
1560 const VirtualPodLeafMap &leafValues
1562 if (
PodType nestedPodTy = dyn_cast<PodType>(recordType)) {
1564 for (RecordAttr record : nestedPodTy.getRecords()) {
1565 recordChain.push_back(record.getName());
1567 rebuildFlattenedPodRecord(bldr, loc, record.getType(), recordChain, leafValues);
1568 genWrite(bldr, loc, nestedPod, record.getName(), recordValue);
1569 recordChain.pop_back();
1574 if (
ArrayType arrTy = splittablePodArray(recordType)) {
1575 if (!arrTy.hasStaticShape()) {
1576 SmallVector<RecordChain> splitIds;
1577 SmallVector<Type> splitTypes;
1578 collectConvertedPodArrayRecordInfos(arrTy, splitIds, splitTypes);
1580 SmallVector<Value> leafArrays;
1581 leafArrays.reserve(splitIds.size());
1582 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
1583 auto it = leafValues.find(
id.withPrefix(recordChain));
1584 assert(it != leafValues.end() &&
"missing flattened POD array leaf value");
1585 leafArrays.push_back(castValueToTypeIfNeeded(bldr, loc, it->second, splitType));
1588 return UnrealizedConversionCastOp::create(bldr, loc, TypeRange {arrTy}, leafArrays)
1594 assert(subIndices &&
"static-shape arrays must provide subelement indices");
1597 for (ArrayAttr index : *subIndices) {
1598 VirtualPodLeafMap elementLeafValues;
1599 SmallVector<StringAttr> elementRecordChain;
1600 forEachPodLeaf(elemPodTy, elementRecordChain, [&](
const RecordChain &
id, Type) {
1601 auto it = leafValues.find(
id.withPrefix(recordChain));
1602 assert(it != leafValues.end() &&
"missing flattened POD array leaf value");
1607 SmallVector<StringAttr> nestedChain;
1608 for (RecordAttr record : elemPodTy.getRecords()) {
1609 nestedChain.push_back(record.getName());
1611 rebuildFlattenedPodRecord(bldr, loc, record.getType(), nestedChain, elementLeafValues);
1612 genWrite(bldr, loc, elementPod, record.getName(), recordValue);
1613 nestedChain.pop_back();
1615 genArrayWrite(bldr, loc, rebuiltArray, index, elementPod);
1617 return rebuiltArray;
1620 auto it = leafValues.find(RecordChain(recordChain));
1621 assert(it != leafValues.end() &&
"missing flattened POD leaf value");
1626static Value peelVirtualPodCompatibilityCasts(Value value) {
1628 value =
cast.getInput();
1634static const VirtualPodLeafMap *
1635lookupVirtualPodLeafMap(Value podValue,
const VirtualPodValueMap &virtualPods) {
1636 podValue = peelVirtualPodCompatibilityCasts(podValue);
1637 auto it = virtualPods.find(podValue);
1638 return it != virtualPods.end() ? &it->second :
nullptr;
1642inline static VirtualPodValueMap::iterator
1643lookupVirtualPodLeafMapIt(Value podValue, VirtualPodValueMap &virtualPods) {
1644 return virtualPods.find(peelVirtualPodCompatibilityCasts(podValue));
1648static SmallVector<Value> orderedVirtualPodLeafValues(
1649 PodType podTy, Location loc, OpBuilder &bldr,
const VirtualPodLeafMap &leafValues
1651 SmallVector<Value> orderedValues;
1652 SmallVector<StringAttr> recordChain;
1655 [&leafValues, &orderedValues, &bldr, loc](
const RecordChain &
id, Type leafType) {
1656 auto it = leafValues.find(
id);
1657 assert(it != leafValues.end() &&
"missing virtual POD leaf value");
1658 orderedValues.push_back(castValueToTypeIfNeeded(bldr, loc, it->second, leafType));
1661 return orderedValues;
1666static void processInputOperand(
1667 Location loc, Value operand, SmallVector<Value> &newOperands, OpBuilder &rewriter,
1668 Operation *userOp =
nullptr,
const VirtualPodValueMap *virtualPods =
nullptr
1670 if (
PodType pt = splittablePod(operand.getType())) {
1672 if (
const VirtualPodLeafMap *leafValues = lookupVirtualPodLeafMap(operand, *virtualPods);
1673 leafValues && (!userOp || !hasEarlierWriteToPod(userOp, operand))) {
1675 newOperands, orderedVirtualPodLeafValues(pt, loc, rewriter, *leafValues)
1680 SmallVector<StringAttr> recordChain;
1681 forEachPodLeaf(pt, recordChain, [&](
const RecordChain &
id, Type) {
1682 newOperands.push_back(genReadAlongPath(rewriter, loc, operand,
id));
1685 newOperands.push_back(operand);
1691static void processInputOperands(
1692 ValueRange operands, MutableOperandRange outputOpRef, Operation *op,
1693 ConversionPatternRewriter &rewriter,
const VirtualPodValueMap *virtualPods =
nullptr
1695 SmallVector<Value> newOperands;
1696 for (Value v : operands) {
1697 processInputOperand(op->getLoc(), v, newOperands, rewriter, op, virtualPods);
1699 rewriter.modifyOpInPlace(op, [&outputOpRef, &newOperands]() {
1700 outputOpRef.assign(ValueRange(newOperands));
1706 if (
PodType lhsTy = splittablePod(peelUnifiableCasts(op.
getLhs()).getType())) {
1709 return splittablePod(peelUnifiableCasts(op.
getRhs()).getType());
1713static void setInsertionPointAfterValueDefinition(Value value, OpBuilder &bldr) {
1714 if (Operation *defOp = value.getDefiningOp()) {
1715 bldr.setInsertionPointAfter(defOp);
1717 auto blockArg = llvm::cast<BlockArgument>(value);
1718 bldr.setInsertionPointToStart(blockArg.getOwner());
1723template <
typename EmitValuesFn>
1724inline static void materializeCompatibleValuesAfterDefinition(
1725 Location loc, Value
source, ArrayRef<Type> targetTypes, OpBuilder &rewriter,
1726 SmallVectorImpl<Value> &out, EmitValuesFn &&emitValues
1728 if (!targetTypes.empty()) {
1729 OpBuilder::InsertionGuard guard(rewriter);
1730 setInsertionPointAfterValueDefinition(
source, rewriter);
1731 emitValues(loc,
source, targetTypes, rewriter, out);
1737getCompatibleMaterializationSource(Value
source, Type compatibleType,
const char *assertMessage) {
1745template <
typename CollectTypesFn>
1746static ArrayRef<Value> getOrMaterializeCompatibleLeafValues(
1747 Location location, Value
source, Type shapeTy, OpBuilder &rewriter,
1748 CompatiblePodLeafMaterializationMap &materializedLeaves, CollectTypesFn &&collectTypes,
1749 const char *assertMessage
1751 source = getCompatibleMaterializationSource(
source, shapeTy, assertMessage);
1752 CompatiblePodLeafMaterializationKey key {
source, shapeTy};
1753 auto [it, inserted] = materializedLeaves.try_emplace(key);
1758 SmallVector<Type> leafTypes;
1759 collectTypes(leafTypes);
1760 materializeCompatibleValuesAfterDefinition(
1761 location,
source, leafTypes, rewriter, it->second,
1762 [](Location loc, Value src, ArrayRef<Type> targetTypes, OpBuilder &bldr,
1763 SmallVectorImpl<Value> &out) {
1764 auto splitCast = UnrealizedConversionCastOp::create(bldr, loc, TypeRange(targetTypes), src);
1765 llvm::append_range(out, splitCast.getResults());
1777static ArrayRef<Value> getOrMaterializeCompatiblePodLeafValues(
1778 Location loc, Value
source,
PodType podTy, OpBuilder &rewriter,
1779 CompatiblePodLeafMaterializationMap &materializedLeaves
1781 return getOrMaterializeCompatibleLeafValues(
1782 loc,
source, podTy, rewriter, materializedLeaves, [&podTy](SmallVector<Type> &leafTypes) {
1783 splitPodTypeTo(podTy, leafTypes);
1784 },
"materialized POD leaves require a source type compatible with the POD shape"
1789static ArrayRef<Value> getOrMaterializeCompatiblePodArrayLeafValues(
1791 CompatiblePodLeafMaterializationMap &materializedLeaves
1793 return getOrMaterializeCompatibleLeafValues(
1794 loc,
source, arrTy, rewriter, materializedLeaves, [&arrTy](SmallVector<Type> &leafTypes) {
1795 splitPodArrayTypeTo(arrTy, leafTypes);
1796 },
"materialized POD-array leaves require a source type compatible with the array shape"
1802static SmallVector<Value> materializeCompatiblePodArrayConvertedValues(
1805 SmallVector<RecordChain> splitIds;
1806 SmallVector<Type> convertedTypes;
1807 collectConvertedPodArrayRecordInfos(arrTy, splitIds, convertedTypes);
1808 source = getCompatibleMaterializationSource(
1810 "materialized POD-array components require a source type compatible with the "
1814 SmallVector<Value> convertedValues;
1815 materializeCompatibleValuesAfterDefinition(
1816 location,
source, convertedTypes, rewriter, convertedValues,
1817 [](Location loc, Value src, ArrayRef<Type> targetTypes, OpBuilder &bldr,
1818 SmallVectorImpl<Value> &out) {
1819 if (targetTypes.size() == 1) {
1820 out.push_back(castValueToTypeIfNeeded(bldr, loc, src, targetTypes.front()));
1826 auto splitCast = UnrealizedConversionCastOp::create(bldr, loc, TypeRange(targetTypes), src);
1827 llvm::append_range(out, splitCast.getResults());
1830 return convertedValues;
1834static void collectWholePodEqualityOperandLeaves(
1835 Location loc, Value operand,
PodType podTy, SmallVector<Value> &leaves, OpBuilder &rewriter,
1836 CompatiblePodLeafMaterializationMap &materializedLeaves, Operation *userOp =
nullptr,
1837 const VirtualPodValueMap *virtualPods =
nullptr
1839 operand = peelUnifiableCasts(operand);
1840 if (splittablePod(operand.getType())) {
1841 processInputOperand(loc, operand, leaves, rewriter, userOp, virtualPods);
1847 "whole-POD equality operand must unify with the concrete POD shape"
1851 getOrMaterializeCompatiblePodLeafValues(loc, operand, podTy, rewriter, materializedLeaves)
1856static LogicalResult splitWholePodEmitEquality(
1858 CompatiblePodLeafMaterializationMap &materializedLeaves, Operation *userOp =
nullptr,
1859 const VirtualPodValueMap *virtualPods =
nullptr
1861 PodType podTy = getWholePodEqualityType(op);
1862 assert(podTy &&
"expected a whole-POD equality to expose a concrete POD side");
1864 SmallVector<Value> lhsLeaves;
1865 SmallVector<Value> rhsLeaves;
1866 collectWholePodEqualityOperandLeaves(
1867 op.getLoc(), op.
getLhs(), podTy, lhsLeaves, rewriter, materializedLeaves, userOp, virtualPods
1869 collectWholePodEqualityOperandLeaves(
1870 op.getLoc(), op.
getRhs(), podTy, rhsLeaves, rewriter, materializedLeaves, userOp, virtualPods
1873 assert(lhsLeaves.size() == rhsLeaves.size() &&
"POD equality leaves must stay aligned");
1874 for (
auto [lhsLeaf, rhsLeaf] : llvm::zip_equal(lhsLeaves, rhsLeaves)) {
1875 if (failed(rejectRaggedNestedLeafEquality(op, lhsLeaf, rhsLeaf))) {
1882 rewriter.eraseOp(op);
1892static Value createVirtualPodPlaceholder(
1893 OpBuilder &bldr, Location loc,
PodType podTy,
const VirtualPodLeafMap &leafValues
1899 SmallVector<Value> orderedValues = orderedVirtualPodLeafValues(podTy, loc, bldr, leafValues);
1900 return UnrealizedConversionCastOp::create(bldr, loc, TypeRange {podTy}, orderedValues)
1906materializeVirtualPod(OpBuilder &bldr,
NewPodOp pod,
const VirtualPodLeafMap &leafValues) {
1907 Location loc =
pod.getLoc();
1909 SmallVector<StringAttr> recordChain;
1910 for (RecordAttr record : podTy.
getRecords()) {
1911 recordChain.push_back(record.getName());
1913 rebuildFlattenedPodRecord(bldr, loc, record.getType(), recordChain, leafValues);
1914 genWrite(bldr, loc,
pod, record.getName(), recordValue);
1915 recordChain.pop_back();
1926findVirtualPodMaterializationAnchor(
NewPodOp pod,
const VirtualPodLeafMap &leafValues) {
1927 Operation *anchor =
pod.getOperation();
1928 Block *block = anchor->getBlock();
1930 for (
const auto &it : leafValues) {
1931 Operation *defOp = it.second.getDefiningOp();
1932 if (!defOp || defOp->getBlock() != block) {
1935 if (anchor->isBeforeInBlock(defOp)) {
1947inline static bool isInsideSupportedScfRegion(Operation *op) {
1948 return op->getParentOfType<scf::IfOp, scf::ForOp, scf::WhileOp>();
1953static bool canResolveVirtualPodRead(
ReadPodOp op,
const VirtualPodValueMap &virtualPods) {
1954 const VirtualPodLeafMap *leafValues = lookupVirtualPodLeafMap(op.
getPodRef(), virtualPods);
1959 if (
auto nestedPodTy = llvm::dyn_cast<PodType>(recType)) {
1961 bool supported =
true;
1962 forEachPodLeaf(nestedPodTy, recordChain, [leafValues, &supported](
const RecordChain &
id, Type) {
1963 Value leaf = leafValues->at(
id);
1971 return llvm::isa<PodType>(recType) || !splittablePodArray(recType);
1977static bool canResolveVirtualPodWrite(
WritePodOp op,
const VirtualPodValueMap &virtualPods) {
1978 if (!lookupVirtualPodLeafMap(op.
getPodRef(), virtualPods) ||
1979 isInsideSupportedScfRegion(op.getOperation())) {
1984 if (
auto nestedPodTy = llvm::dyn_cast<PodType>(recordType)) {
1985 const VirtualPodLeafMap *sourceLeaves = lookupVirtualPodLeafMap(op.
getValue(), virtualPods);
1986 SmallVector<StringAttr> recordChain;
1987 bool supported =
true;
1989 nestedPodTy, recordChain, [sourceLeaves, &supported](
const RecordChain &
id, Type leafType) {
1990 Type copyType = sourceLeaves ? sourceLeaves->at(
id).getType() : leafType;
1996 if (
ArrayType arrTy = splittablePodArray(recordType)) {
1997 SmallVector<RecordChain> splitIds;
1998 SmallVector<Type> splitTypes;
1999 collectConvertedPodArrayRecordInfos(arrTy, splitIds, splitTypes);
2006inline static bool shouldDeferPodArrayReadToStep3(
ReadArrayOp op) {
2008 llvm::isa_and_present<ReadPodOp>(op.
getArrRef().getDefiningOp());
2012static ReadPodOp getReadPodBacking(Value value) {
2013 value = peelUnifiableCasts(value);
2018 auto cast = value.getDefiningOp<UnrealizedConversionCastOp>();
2019 if (!
cast ||
cast->getNumOperands() != 1) {
2022 return peelUnifiableCasts(
cast.getOperand(0)).getDefiningOp<
ReadPodOp>();
2027static void materializeFreshPodArrayMutationSnapshots(ModuleOp module) {
2028 SmallVector<ReadPodOp> reads;
2029 module.walk([&reads](ReadPodOp read) {
2030 if (!splittablePodArray(read.getType()) || !isFreshUnwrittenPodRead(read)) {
2033 if (llvm::any_of(read->getUsers(), [](Operation *user) {
2034 return isa<WriteArrayOp, InsertArrayOp>(user);
2036 reads.push_back(read);
2040 OpBuilder builder(read);
2041 std::optional<Value> snapshot =
2042 tryMaterializeFreshUnwrittenDirectRecordRead(builder, read.getLoc(), read);
2043 assert(snapshot &&
"fresh array reads have independent default storage");
2044 read.replaceAllUsesWith(*snapshot);
2050static LogicalResult rejectUnsupportedRaggedSnapshots(ModuleOp module) {
2051 WalkResult result =
module.walk([](UnrealizedConversionCastOp cast) -> WalkResult {
2052 if (cast->getNumResults() != 1 || getTaggedRaggedNestedLeafKind(cast.getResult(0)).empty() ||
2053 llzk::canMaterializeValueCopy(cast.getResult(0).getType())) {
2054 return WalkResult::advance();
2056 return cast.emitError(
"cannot copy nested array leaf: ")
2059 return failure(result.wasInterrupted());
2064static LogicalResult rejectUnsupportedPodReadArrayMutations(ModuleOp modOp) {
2065 auto rejectMutation = [](Operation *op, Value
array,
ArrayType arrayType) -> LogicalResult {
2068 isFreshUnwrittenPodRead(readOp)) {
2072 return op->emitOpError() <<
"cannot mutate array-of-POD value read from POD record '"
2077 auto result = modOp.walk([rejectMutation](Operation *op) -> WalkResult {
2078 LogicalResult status = TypeSwitch<Operation *, LogicalResult>(op)
2080 return rejectMutation(writeOp, writeOp.getArrRef(), writeOp.getArrRefType());
2082 .Case<InsertArrayOp>([rejectMutation](
InsertArrayOp insertOp) {
2084 }).Default([](Operation *) {
return success(); });
2087 return failure(result.wasInterrupted());
2091inline static bool shouldDeferPodArrayLengthToStep3(
ArrayLengthOp op) {
2096static SmallVector<std::string> getSplitRecordNameSuffixes(Type type) {
2097 SmallVector<std::string> suffixes;
2098 if (
PodType pt = splittablePod(type)) {
2099 SmallVector<StringAttr> recordChain;
2100 forEachPodLeaf(pt, recordChain, [&suffixes](
const RecordChain &
id, Type) {
2102 llvm::raw_string_ostream os(suffix);
2103 for (
size_t i = 0; i <
id.nameList.size(); ++i) {
2104 os <<
'.' <<
id.getComponentName(i);
2106 suffixes.push_back(std::move(suffix));
2113static LogicalResult updateVirtualPodRecordLeafValues(
2114 Location loc, StringAttr recordName, Type recordType, Value recordValue,
2115 const VirtualPodValueMap &virtualPods, RewriterBase &rewriter, VirtualPodLeafMap &leafValues
2117 SmallVector<StringAttr> prefix {recordName};
2119 auto snapshot = [&rewriter, loc](Value value) -> FailureOr<Value> {
2126 if (
PodType nestedPodTy = llvm::dyn_cast<PodType>(recordType)) {
2127 if (
const VirtualPodLeafMap *nestedLeafValues =
2128 lookupVirtualPodLeafMap(recordValue, virtualPods)) {
2129 SmallVector<StringAttr> nestedRecordChain;
2130 bool failedToCopy =
false;
2131 forEachPodLeaf(nestedPodTy, nestedRecordChain, [&](
const RecordChain &
id, Type) {
2132 FailureOr<Value> copied = snapshot(nestedLeafValues->at(
id));
2133 if (failed(copied)) {
2134 failedToCopy =
true;
2137 leafValues[
id.withPrefix(prefix)] = *copied;
2139 return failure(failedToCopy);
2142 bool failedToCopy =
false;
2143 SmallVector<StringAttr> nestedRecordChain;
2144 forEachPodLeaf(nestedPodTy, nestedRecordChain, [&](
const RecordChain &
id, Type) {
2145 FailureOr<Value> copied = snapshot(genReadAlongPath(rewriter, loc, recordValue,
id));
2146 if (failed(copied)) {
2147 failedToCopy =
true;
2150 leafValues[
id.withPrefix(prefix)] = *copied;
2152 return failure(failedToCopy);
2155 if (
ArrayType arrTy = splittablePodArray(recordType)) {
2156 SmallVector<RecordChain> splitIds;
2157 SmallVector<Type> splitTypes;
2158 collectConvertedPodArrayRecordInfos(arrTy, splitIds, splitTypes);
2159 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2160 Value leaf = castValueToTypeIfNeeded(
2161 rewriter, loc, genReadAlongPath(rewriter, loc, recordValue,
id), splitType
2163 FailureOr<Value> copied = snapshot(leaf);
2164 if (failed(copied)) {
2167 leafValues[
id.withPrefix(prefix)] = *copied;
2172 FailureOr<Value> copied =
2173 snapshot(castValueToTypeIfNeeded(rewriter, loc, recordValue, recordType));
2174 if (failed(copied)) {
2177 leafValues[RecordChain(prefix)] = *copied;
2182inline static void baseTargetSetup(ConversionTarget &target) {
2183 target.addLegalDialect<
2189 target.addLegalOp<ModuleOp>();
2194class NondetToNewPod :
public OpConversionPattern<NonDetOp> {
2195 using OpConversionPattern<NonDetOp>::OpConversionPattern;
2196 LogicalResult matchAndRewrite(
2197 NonDetOp nondetOp, OpAdaptor, ConversionPatternRewriter &rewriter
2199 if (
auto pt = dyn_cast<PodType>(nondetOp.getType())) {
2209static LogicalResult step0(ModuleOp modOp) {
2210 MLIRContext *ctx = modOp.getContext();
2212 PassManager prepPM(ctx);
2215 if (failed(prepPM.run(modOp))) {
2219 RewritePatternSet patterns {ctx};
2220 patterns.add<NondetToNewPod>(ctx);
2221 ConversionTarget target {*ctx};
2223 baseTargetSetup(target);
2224 target.addLegalOp<UnrealizedConversionCastOp>();
2225 target.addDynamicallyLegalOp<
NonDetOp>([](
NonDetOp op) {
return !isa<PodType>(op.getType()); });
2227 return applyFullConversion(modOp, target, std::move(patterns));
2231using MemberInfo = std::pair<StringAttr, Type>;
2233using LocalMemberReplacementMap = DenseMap<RecordChain, MemberInfo>;
2235using MemberReplacementMap = DenseMap<StructDefOp, DenseMap<StringAttr, LocalMemberReplacementMap>>;
2238inline static StringAttr getSplitPodArrayShapeMemberName(MLIRContext *ctx, StringAttr memberName) {
2239 return StringAttr::get(ctx, (memberName.getValue() +
"_shape").str());
2243static void flattenPodMemberIntoLeaves(
2245 LocalMemberReplacementMap &localRepMapRef, SymbolTable &structSymbolTable,
2246 ConversionPatternRewriter &rewriter
2248 forEachPodLeaf(podTy, recordChain, [&](
const RecordChain &
id, Type ty) {
2250 id.getFlattenedMemberName(originalMember.getContext(), originalMember.
getSymNameAttr());
2252 rewriter, originalMember.getLoc(), name, ty,
2253 !
id.syntheticShapeCarrier && originalMember.
getSignal(),
2254 !
id.syntheticShapeCarrier && originalMember.
getColumn()
2258 localRepMapRef[id] = std::make_pair(structSymbolTable.insert(newMember), ty);
2266class SplitPodInMemberDefOp :
public OpConversionPattern<MemberDefOp> {
2267 SymbolTableCollection &tables;
2268 MemberReplacementMap &repMapRef;
2271 SplitPodInMemberDefOp(
2272 MLIRContext *ctx, SymbolTableCollection &symTables, MemberReplacementMap &memberRepMap
2274 : OpConversionPattern<MemberDefOp>(ctx), tables(symTables), repMapRef(memberRepMap) {}
2276 inline static bool legal(MemberDefOp op) {
return !splittablePod(op.
getType()); }
2278 LogicalResult matchAndRewrite(
2279 MemberDefOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
2284 StructDefOp inStruct = op->getParentOfType<StructDefOp>();
2286 LocalMemberReplacementMap &localRepMapRef = repMapRef[inStruct][op.
getSymNameAttr()];
2288 PodType podTy = llvm::cast<PodType>(adaptor.getType());
2290 SymbolTable &structSymbolTable = tables.getSymbolTable(inStruct);
2291 SmallVector<StringAttr> recordChain;
2292 flattenPodMemberIntoLeaves(op, podTy, recordChain, localRepMapRef, structSymbolTable, rewriter);
2293 rewriter.eraseOp(op);
2299class SplitPodArrayInMemberDefOp :
public OpConversionPattern<MemberDefOp> {
2300 SymbolTableCollection &tables;
2301 MemberReplacementMap &repMapRef;
2304 SplitPodArrayInMemberDefOp(
2305 MLIRContext *ctx, SymbolTableCollection &symTables, MemberReplacementMap &memberRepMap
2307 : OpConversionPattern<MemberDefOp>(ctx), tables(symTables), repMapRef(memberRepMap) {}
2309 inline static bool legal(MemberDefOp op) {
return !splittablePodArray(op.
getType()); }
2311 LogicalResult matchAndRewrite(
2312 MemberDefOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
2317 StructDefOp inStruct = op->getParentOfType<StructDefOp>();
2319 LocalMemberReplacementMap &localRepMapRef = repMapRef[inStruct][op.
getSymNameAttr()];
2321 ArrayType arrTy = llvm::cast<ArrayType>(adaptor.getType());
2322 SmallVector<RecordChain> splitIds;
2323 SmallVector<Type> splitTypes;
2324 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
2325 if (splitTypes.empty()) {
2326 ArrayType carrierTy = getPodArrayShapeCarrierType(arrTy);
2327 rewriter.modifyOpInPlace(op, [&]() {
2331 localRepMapRef[RecordChain()] = std::make_pair(op.
getSymNameAttr(), carrierTy);
2335 SymbolTable &structSymbolTable = tables.getSymbolTable(inStruct);
2336 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2337 StringAttr name =
id.getFlattenedMemberName(op.getContext(), op.
getSymNameAttr());
2343 localRepMapRef[id] = std::make_pair(structSymbolTable.insert(newMember), splitType);
2345 if (needsPodArrayShapeCarrier(arrTy)) {
2346 ArrayType carrierTy = getPodArrayShapeCarrierType(arrTy);
2347 StringAttr carrierName =
2348 getSplitPodArrayShapeMemberName(op.getContext(), op.
getSymNameAttr());
2349 MemberDefOp carrierMember =
2353 localRepMapRef[RecordChain()] =
2354 std::make_pair(structSymbolTable.insert(carrierMember), carrierTy);
2356 rewriter.eraseOp(op);
2364step1(ModuleOp modOp, SymbolTableCollection &symTables, MemberReplacementMap &memberRepMap) {
2365 MLIRContext *ctx = modOp.getContext();
2367 RewritePatternSet patterns(ctx);
2369 patterns.add<SplitPodInMemberDefOp, SplitPodArrayInMemberDefOp>(ctx, symTables, memberRepMap);
2371 ConversionTarget target(*ctx);
2372 baseTargetSetup(target);
2373 target.addLegalOp<UnrealizedConversionCastOp>();
2375 return SplitPodInMemberDefOp::legal(op) && SplitPodArrayInMemberDefOp::legal(op);
2378 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 1: split pod-type and array-of-pod members\n";);
2379 return applyFullConversion(modOp, target, std::move(patterns));
2387class PodArrayTypeConverter :
public TypeConverter {
2389 PodArrayTypeConverter() {
2390 addConversion([](Type type) {
return type; });
2392 [](ArrayType arrTy, SmallVectorImpl<Type> &results) -> std::optional<LogicalResult> {
2393 if (!splittablePodArray(arrTy)) {
2394 return std::nullopt;
2396 convertPodArrayTypeTo(arrTy, results);
2401 auto materializeCast = [](OpBuilder &bldr, Type targetType, ValueRange inputs,
2402 Location loc) -> Value {
2403 if (inputs.size() != 1 || !
typesUnify(inputs.front().getType(), targetType)) {
2406 return castValueToTypeIfNeeded(bldr, loc, inputs.front(), targetType);
2408 addTargetMaterialization(materializeCast);
2409 addSourceMaterialization(materializeCast);
2414class SplitPodArrayNonDetOp :
public OpConversionPattern<NonDetOp> {
2416 using OpConversionPattern<NonDetOp>::OpConversionPattern;
2418 static bool legal(NonDetOp op) {
return !splittablePodArray(op.getType()); }
2421 matchAndRewrite(NonDetOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
2425 SmallVector<Type> splitTypes;
2426 splitPodArrayTypeTo(op.getType(), splitTypes);
2427 if (splitTypes.empty()) {
2429 rewriter, op, getPodArrayShapeCarrierType(llvm::cast<ArrayType>(op.getType()))
2433 SmallVector<Value> replacements;
2434 ArrayType arrTy = llvm::cast<ArrayType>(op.getType());
2435 replacements.reserve(splitTypes.size() + (needsPodArrayShapeCarrier(arrTy) ? 1 : 0));
2436 for (Type splitType : splitTypes) {
2437 replacements.push_back(
2441 if (needsPodArrayShapeCarrier(arrTy)) {
2443 op,
NonDetOp::create(rewriter, op.getLoc(), getPodArrayShapeCarrierType(arrTy))
2446 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
2461class SplitPodArrayCreateArrayOp :
public OpConversionPattern<CreateArrayOp> {
2463 using OpConversionPattern<CreateArrayOp>::OpConversionPattern;
2465 static bool legal(CreateArrayOp op) {
return !splittablePodArray(op.getType()); }
2467 LogicalResult matchAndRewrite(
2468 CreateArrayOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
2473 ArrayType arrTy = llvm::cast<ArrayType>(op.getType());
2474 SmallVector<RecordChain> splitIds;
2475 SmallVector<Type> splitTypes;
2476 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
2477 if (splitTypes.empty()) {
2478 ArrayType carrierTy = getPodArrayShapeCarrierType(arrTy);
2479 if (adaptor.getMapOperands().empty()) {
2484 FlattenedConvertedValueRangeStorage mapOperands(adaptor.getMapOperands());
2491 SmallVector<Value> replacements;
2492 replacements.reserve(splitTypes.size() + (needsPodArrayShapeCarrier(arrTy) ? 1 : 0));
2495 if (adaptor.getElements().empty()) {
2496 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2497 ArrayType preciseSplitType = llvm::cast<ArrayType>(splitType);
2498 replacements.push_back(
2499 createSplitPodArrayReplacement(op, op.getLoc(), arrTy,
id, preciseSplitType, rewriter)
2502 if (needsPodArrayShapeCarrier(arrTy)) {
2503 Value shapeCarrier =
2504 materializeArrayLengthCarrier(op.
getResult(), arrTy, op.getLoc(), rewriter);
2506 replacements.push_back(shapeCarrier);
2508 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
2513 assert(elementIndices &&
"array.new with explicit elements requires a static array shape");
2515 elementIndices->size() == adaptor.getElements().size() &&
2516 "array.new element count must match the outer array cardinality"
2523 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2524 ArrayType preciseSplitType = llvm::cast<ArrayType>(splitType);
2525 ArrayType storageSplitType = getSplitPodArrayStorageType(arrTy,
id.nameList);
2527 SmallVector<Value> leafValues;
2528 leafValues.reserve(adaptor.getElements().size());
2529 for (ValueRange elementRange : adaptor.getElements()) {
2530 Value element = getSingleConvertedValue(elementRange);
2531 leafValues.push_back(genReadAlongPath(rewriter, op.getLoc(), element,
id));
2534 ArrayType materializedType = storageSplitType;
2536 if (storageSplitType != preciseSplitType) {
2537 ArrayInstantiationInfo instantiationInfo;
2538 switch (inferCommonArrayInstantiation(leafValues, instantiationInfo)) {
2539 case CommonArrayInstantiationStatus::conflict:
2541 return op.emitOpError(
2542 "with POD elements having conflicting affine map instantiations cannot be promoted "
2543 "to higher dimensional array"
2545 case CommonArrayInstantiationStatus::inferred: {
2546 materializedType = preciseSplitType;
2547 SmallVector<ValueRange> mapOperands;
2548 mapOperands.reserve(instantiationInfo.mapOperandStorage.size());
2549 for (
const SmallVector<Value> &values : instantiationInfo.mapOperandStorage) {
2550 mapOperands.push_back(values);
2553 rewriter, op.getLoc(), materializedType, mapOperands,
2554 instantiationInfo.numDimsPerMap
2557 splitArray = splitArrayOp;
2560 case CommonArrayInstantiationStatus::unavailable:
2566 splitArray = createWritableArrayValue(rewriter, op.getLoc(), materializedType);
2570 for (
auto [index, leafValue] : llvm::zip_equal(*elementIndices, leafValues)) {
2571 genArrayWrite(rewriter, op.getLoc(), splitArray, index, leafValue);
2573 replacements.push_back(
2574 castValueToTypeIfNeeded(rewriter, op.getLoc(), splitArray, preciseSplitType)
2578 FlattenedConvertedValueRangeStorage mapOperands(adaptor.getMapOperands());
2579 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2580 ArrayType preciseSplitType = llvm::cast<ArrayType>(splitType);
2581 replacements.push_back(createSplitPodArrayReplacement(
2582 op, op.getLoc(), arrTy,
id, preciseSplitType, rewriter, mapOperands.ranges,
2588 if (needsPodArrayShapeCarrier(arrTy)) {
2589 Value shapeCarrier =
2590 materializeArrayLengthCarrier(op.
getResult(), arrTy, op.getLoc(), rewriter);
2592 replacements.push_back(shapeCarrier);
2595 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
2601class SplitPodArrayReadArrayOp :
public OpConversionPattern<ReadArrayOp> {
2603 using OpConversionPattern<ReadArrayOp>::OpConversionPattern;
2605 static bool legal(ReadArrayOp op) {
2606 return !splittablePodArray(op.
getArrRefType()) || shouldDeferPodArrayReadToStep3(op);
2609 LogicalResult matchAndRewrite(
2610 ReadArrayOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
2617 SmallVector<RecordChain> splitIds;
2618 SmallVector<Type> splitTypes;
2619 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
2620 if (splitTypes.empty()) {
2625 SmallVector<Value> indices = flattenConvertedValues(adaptor.getIndices());
2628 VirtualPodLeafMap leafValues;
2629 auto splitArrRefs = adaptor.getArrRef().take_front(splitIds.size());
2630 for (
auto [
id, splitType, splitArrRange] :
2631 llvm::zip_equal(splitIds, splitTypes, splitArrRefs)) {
2633 rewriter, op.getLoc(), getSingleConvertedValue(splitArrRange), indices
2636 leafValues[id] = tagRaggedNestedLeafValue(
2637 rewriter, op.getLoc(), leafValue, getRaggedNestedLeafAttrName(arrTy, splitType)
2641 SmallVector<StringAttr> recordChain;
2642 for (RecordAttr record : podTy.
getRecords()) {
2643 recordChain.push_back(record.getName());
2644 Value recordValue = rebuildFlattenedPodRecord(
2645 rewriter, op.getLoc(), record.getType(), recordChain, leafValues
2647 genWrite(rewriter, op.getLoc(), pod, record.getName(), recordValue);
2648 recordChain.pop_back();
2650 rewriter.replaceOp(op, pod);
2656class SplitPodArrayWriteArrayOp :
public OpConversionPattern<WriteArrayOp> {
2658 using OpConversionPattern<WriteArrayOp>::OpConversionPattern;
2660 static bool legal(WriteArrayOp op) {
return !splittablePodArray(op.
getArrRefType()); }
2662 LogicalResult matchAndRewrite(
2663 WriteArrayOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
2669 SmallVector<RecordChain> splitIds;
2670 SmallVector<Type> splitTypes;
2671 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
2672 if (splitTypes.empty()) {
2673 rewriter.eraseOp(op);
2677 SmallVector<Value> indices = flattenConvertedValues(adaptor.getIndices());
2678 Value podValue = getSingleConvertedValue(adaptor.getRvalue());
2679 auto splitArrRefs = adaptor.getArrRef().take_front(splitIds.size());
2680 for (
auto [
id, splitArrRange, splitType] :
2681 llvm::zip_equal(splitIds, splitArrRefs, splitTypes)) {
2682 Value leafValue = genReadAlongPath(rewriter, op.getLoc(), podValue,
id);
2685 rewriter, op.getLoc(), getSingleConvertedValue(splitArrRange), indices, leafValue
2689 rewriter.eraseOp(op);
2695class SplitPodArrayInFuncDefOp :
public OpConversionPattern<FuncDefOp> {
2697 using OpConversionPattern<FuncDefOp>::OpConversionPattern;
2699 static bool legal(FuncDefOp op) {
2705 matchAndRewrite(FuncDefOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
2709 const auto *tyConv = getTypeConverter();
2710 assert(tyConv &&
"expected pod-array type converter");
2713 TypeConverter::SignatureConversion inputConversion(oldTy.getNumInputs());
2714 if (failed(tyConv->convertSignatureArgs(oldTy.getInputs(), inputConversion))) {
2715 return rewriter.notifyMatchFailure(op,
"failed to convert array-of-pod inputs");
2718 SmallVector<Type> newResults;
2719 if (failed(tyConv->convertTypes(oldTy.getResults(), newResults))) {
2720 return rewriter.notifyMatchFailure(op,
"failed to convert array-of-pod results");
2724 failed(rewriter.convertRegionTypes(&op.
getBody(), *tyConv, &inputConversion))) {
2725 return rewriter.notifyMatchFailure(op,
"failed to convert function body block arguments");
2728 SmallVector<size_t> originalInputIdxToSize, originalResultIdxToSize;
2729 SmallVector<Type> newInputs = convertPodArrayTypes(oldTy.getInputs(), &originalInputIdxToSize);
2730 SmallVector<Type> newResultsWithSizeInfo =
2731 convertPodArrayTypes(oldTy.getResults(), &originalResultIdxToSize);
2733 newResultsWithSizeInfo == newResults &&
2734 "expected array-of-pod type conversion to match function result attr replication"
2736 SplitFunctionNameInfo inputNameInfo =
2738 return op.getArgNameAttr(i);
2739 }, getSplitPodArrayRecordNameSuffixes);
2740 ArrayAttr resultAttrs = op.getAllResultAttrs();
2741 SplitFunctionNameInfo resultNameInfo =
2743 return getAttrAtIndexWithName(resultAttrs, i, RES_NAME_ATTR_NAME);
2744 }, getSplitPodArrayRecordNameSuffixes);
2746 rewriter.modifyOpInPlace(op, [&]() {
2747 op.
setFunctionType(FunctionType::get(op.getContext(), newInputs, newResults));
2773static void collectSplitPodArrayOperandValues(
2774 Location loc, Value originalOperand, ValueRange convertedValues,
2775 SmallVectorImpl<Value> &newOperands, ConversionPatternRewriter &rewriter
2777 ArrayType arrTy = splittablePodArray(originalOperand.getType());
2779 llvm::append_range(newOperands, convertedValues);
2783 SmallVector<RecordChain> splitIds;
2784 SmallVector<Type> splitTypes;
2785 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
2786 if (splitTypes.empty()) {
2787 if (Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, convertedValues)) {
2788 newOperands.push_back(
2789 castValueToTypeIfNeeded(rewriter, loc, carrier, getPodArrayShapeCarrierType(arrTy))
2793 if (!convertedValues.empty()) {
2794 newOperands.push_back(castValueToTypeIfNeeded(
2795 rewriter, loc, getSingleConvertedValue(convertedValues),
2796 getPodArrayShapeCarrierType(arrTy)
2800 newOperands.push_back(materializeArrayLengthCarrier(originalOperand, arrTy, loc, rewriter));
2804 ValueRange leafConvertedValues = getConvertedPodArrayLeafValues(arrTy, convertedValues);
2805 Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, convertedValues);
2807 auto isDirectAggregateToSplitCast = [&leafConvertedValues, &splitTypes]() {
2808 if (leafConvertedValues.empty()) {
2811 auto castOp = leafConvertedValues.front().getDefiningOp<UnrealizedConversionCastOp>();
2812 if (!castOp || castOp->getNumOperands() != 1) {
2815 ArrayType castArrTy = splittablePodArray(castOp.getOperand(0).getType());
2816 size_t expectedResults =
2817 castArrTy ? splitTypes.size() + (needsPodArrayShapeCarrier(castArrTy) ? 1 : 0) : 0;
2818 if (!castArrTy || castOp->getNumResults() != expectedResults) {
2822 return llvm::all_of(llvm::zip_equal(leafConvertedValues, splitTypes), [&castOp](
auto pair) {
2823 Value convertedValue = std::get<0>(pair);
2824 Type splitType = std::get<1>(pair);
2825 return convertedValue.getDefiningOp<UnrealizedConversionCastOp>() == castOp &&
2826 typesUnify(convertedValue.getType(), splitType);
2829 bool directAggregateToSplitCast = isDirectAggregateToSplitCast();
2831 if (!directAggregateToSplitCast && allValueTypesUnifyWithTypes(leafConvertedValues, splitTypes)) {
2832 llvm::append_range(newOperands, leafConvertedValues);
2834 newOperands.push_back(
2835 castValueToTypeIfNeeded(rewriter, loc, carrier, getPodArrayShapeCarrierType(arrTy))
2837 }
else if (needsPodArrayShapeCarrier(arrTy)) {
2838 newOperands.push_back(materializeArrayLengthCarrier(originalOperand, arrTy, loc, rewriter));
2843 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
2844 Value splitValue = genReadAlongPath(rewriter, loc, originalOperand,
id);
2845 newOperands.push_back(castValueToTypeIfNeeded(rewriter, loc, splitValue, splitType));
2847 if (carrier && !directAggregateToSplitCast) {
2848 newOperands.push_back(
2849 castValueToTypeIfNeeded(rewriter, loc, carrier, getPodArrayShapeCarrierType(arrTy))
2851 }
else if (needsPodArrayShapeCarrier(arrTy)) {
2852 RecordChain carrierId({getPodArrayShapeCarrierMarker(rewriter.getContext())},
true);
2853 Value splitCarrier = genReadAlongPath(rewriter, loc, originalOperand, carrierId);
2854 newOperands.push_back(
2855 castValueToTypeIfNeeded(rewriter, loc, splitCarrier, getPodArrayShapeCarrierType(arrTy))
2861class SplitPodArrayInUnifiableCastOp :
public OpConversionPattern<UnifiableCastOp> {
2863 using OpConversionPattern<UnifiableCastOp>::OpConversionPattern;
2865 static bool legal(UnifiableCastOp op) {
2866 return !splittablePodArray(op.getType()) && !splittablePodArray(op.
getInput().getType());
2869 LogicalResult matchAndRewrite(
2870 UnifiableCastOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
2876 ArrayType inputArrTy = splittablePodArray(op.
getInput().getType());
2877 ArrayType resultArrTy = splittablePodArray(op.getType());
2882 if (inputArrTy && !resultArrTy) {
2883 return rewriter.notifyMatchFailure(
2884 op,
"array-of-pod input to non-array result requires aggregate materialization or "
2885 "signature/template rewriting"
2890 return rewriter.notifyMatchFailure(
2891 op,
"generic input to array-of-pod result requires signature/template rewriting"
2895 SmallVector<RecordChain> inputSplitIds;
2896 SmallVector<Type> inputSplitTypes;
2897 splitPodArrayTypeTo(inputArrTy, inputSplitTypes, &inputSplitIds);
2899 SmallVector<RecordChain> resultSplitIds;
2900 SmallVector<Type> resultSplitTypes;
2901 splitPodArrayTypeTo(resultArrTy, resultSplitTypes, &resultSplitIds);
2903 if (inputSplitIds != resultSplitIds) {
2904 return rewriter.notifyMatchFailure(
2905 op,
"array-of-pod cast changed POD leaf structure unexpectedly"
2909 SmallVector<Value> splitInputs;
2910 collectSplitPodArrayOperandValues(
2911 op.getLoc(), op.
getInput(), adaptor.getInput(), splitInputs, rewriter
2913 ValueRange splitInputLeaves = getConvertedPodArrayLeafValues(inputArrTy, splitInputs);
2914 if (resultSplitTypes.empty()) {
2915 if (splitInputs.size() != 1) {
2916 return rewriter.notifyMatchFailure(
2917 op,
"expected one shape carrier for zero-leaf array-of-pod cast"
2920 Value replacement = castValueToTypeIfNeeded(
2921 rewriter, op.getLoc(), splitInputs.front(), getPodArrayShapeCarrierType(resultArrTy)
2923 if (replacement != splitInputs.front()) {
2926 rewriter.replaceOp(op, replacement);
2929 if (splitInputLeaves.size() != resultSplitTypes.size()) {
2930 return rewriter.notifyMatchFailure(
2931 op,
"failed to collect one split input per array-of-pod cast leaf"
2935 SmallVector<Value> replacements;
2936 replacements.reserve(
2937 resultSplitTypes.size() + (needsPodArrayShapeCarrier(resultArrTy) ? 1 : 0)
2939 for (
auto [splitInput, resultSplitType] : llvm::zip_equal(splitInputLeaves, resultSplitTypes)) {
2941 castValueToTypeIfNeeded(rewriter, op.getLoc(), splitInput, resultSplitType);
2942 if (replacement != splitInput) {
2945 replacements.push_back(replacement);
2947 if (needsPodArrayShapeCarrier(resultArrTy)) {
2948 Value carrier = getConvertedPodArrayShapeCarrierIfPresent(inputArrTy, splitInputs);
2950 carrier = materializeArrayLengthCarrier(op.
getInput(), inputArrTy, op.getLoc(), rewriter);
2952 Value replacement = castValueToTypeIfNeeded(
2953 rewriter, op.getLoc(), carrier, getPodArrayShapeCarrierType(resultArrTy)
2955 if (replacement != carrier) {
2958 replacements.push_back(replacement);
2961 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
2967class SplitPodArrayInReturnOp :
public OpConversionPattern<ReturnOp> {
2969 using OpConversionPattern<ReturnOp>::OpConversionPattern;
2971 static bool legal(ReturnOp op) {
2972 return !containsSplittablePodArrayType(op.
getOperands().getTypes());
2975 LogicalResult matchAndRewrite(
2976 ReturnOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
2981 SmallVector<Value> newOperands;
2982 for (
auto [operand, convertedValues] :
2983 llvm::zip_equal(op.
getOperands(), adaptor.getOperands())) {
2984 collectSplitPodArrayOperandValues(
2985 op.getLoc(), operand, convertedValues, newOperands, rewriter
2994class SplitPodArrayInCallOp :
public OpConversionPattern<CallOp> {
2996 using OpConversionPattern<CallOp>::OpConversionPattern;
2998 static bool legal(CallOp op) {
2999 return !containsSplittablePodArrayType(op.
getArgOperands().getTypes()) &&
3000 !containsSplittablePodArrayType(op.getResultTypes());
3003 LogicalResult matchAndRewrite(
3004 CallOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3009 const auto *tyConv = getTypeConverter();
3010 assert(tyConv &&
"expected pod-array type converter");
3012 SmallVector<Type> newResultTypes;
3013 if (failed(tyConv->convertTypes(op.getResultTypes(), newResultTypes))) {
3014 return rewriter.notifyMatchFailure(op,
"failed to convert array-of-pod call results");
3017 FlattenedConvertedValueRangeStorage mapOperands(adaptor.getMapOperands());
3019 SmallVector<Value> newArgOperands;
3020 for (
auto [operand, convertedValues] :
3021 llvm::zip_equal(op.
getArgOperands(), adaptor.getArgOperands())) {
3022 collectSplitPodArrayOperandValues(
3023 op.getLoc(), operand, convertedValues, newArgOperands, rewriter
3027 op.getLoc(), newResultTypes, op, mapOperands.ranges, newArgOperands, rewriter
3030 SmallVector<SmallVector<Value>> replacementStorage;
3031 replacementStorage.reserve(op.getNumResults());
3032 auto newResultIt = newCall.getResults().begin();
3033 for (Type oldResultType : op.getResultTypes()) {
3034 SmallVector<Type> convertedTypes;
3035 (void)convertPodArrayTypeTo(oldResultType, convertedTypes);
3036 SmallVector<Value> replacementsForResult;
3037 replacementsForResult.reserve(convertedTypes.size());
3038 for (
size_t i = 0; i < convertedTypes.size(); ++i) {
3039 replacementsForResult.push_back(*newResultIt);
3042 replacementStorage.push_back(std::move(replacementsForResult));
3045 SmallVector<ValueRange> replacements;
3046 replacements.reserve(replacementStorage.size());
3047 for (
const SmallVector<Value> &values : replacementStorage) {
3048 replacements.push_back(values);
3050 rewriter.replaceOpWithMultiple(op, replacements);
3056static Value getPodArrayEqualityShapeSource(
3057 ArrayType arrTy, Value originalValue, ValueRange convertedValues, Location loc,
3060 if (Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, convertedValues)) {
3064 ValueRange leafValues = getConvertedPodArrayLeafValues(arrTy, convertedValues);
3065 if (!leafValues.empty()) {
3066 return leafValues.front();
3069 if (!convertedValues.empty()) {
3070 return getSingleConvertedValue(convertedValues);
3073 return materializeArrayLengthCarrier(originalValue, arrTy, loc, rewriter);
3077static SmallVector<Value> collectCompatiblePodArrayEqualityLeaves(
3078 Location loc, Value originalValue, ValueRange convertedValues,
ArrayType arrTy,
3079 ArrayType peerArrTy, ConversionPatternRewriter &rewriter,
3080 CompatiblePodLeafMaterializationMap &materializedLeaves
3083 ValueRange leafValues = getConvertedPodArrayLeafValues(arrTy, convertedValues);
3084 size_t leafCount = getSplitPodArrayLeafCount(arrTy);
3085 if (leafValues.size() == leafCount || leafCount == 0) {
3086 return SmallVector<Value>(leafValues.begin(), leafValues.end());
3089 return materializeCompatiblePodArrayLeafValues(loc, originalValue, arrTy, rewriter);
3092 if (!peerArrTy || convertedValues.size() != 1) {
3093 if (!peerArrTy || !convertedValues.empty()) {
3094 return SmallVector<Value>(convertedValues.begin(), convertedValues.end());
3098 Value
source = convertedValues.empty() ? originalValue : getSingleConvertedValue(convertedValues);
3099 SmallVector<Value> materialized;
3101 materialized, getOrMaterializeCompatiblePodArrayLeafValues(
3102 loc,
source, peerArrTy, rewriter, materializedLeaves
3105 return materialized;
3110 if (
ArrayType concreteArrTy = splittablePodArray(arrTy)) {
3111 return concreteArrTy;
3117class SplitPodArrayInEmitEqualityOp :
public OpConversionPattern<constrain::EmitEqualityOp> {
3118 CompatiblePodLeafMaterializationMap &materializedLeaves;
3121 SplitPodArrayInEmitEqualityOp(
3122 TypeConverter &tyConv, MLIRContext *ctx,
3123 CompatiblePodLeafMaterializationMap &materializedLeafMap
3125 : OpConversionPattern<constrain::EmitEqualityOp>(tyConv, ctx),
3126 materializedLeaves(materializedLeafMap) {}
3128 static bool legal(constrain::EmitEqualityOp op) {
3129 return !containsSplittablePodArrayType(op->getOperandTypes());
3132 LogicalResult matchAndRewrite(
3133 constrain::EmitEqualityOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3139 ArrayType lhsTy = splittablePodArray(op.
getLhs().getType());
3140 ArrayType rhsTy = splittablePodArray(op.
getRhs().getType());
3141 StringRef raggedKind = lhsTy ? getRaggedNestedLeafKind(lhsTy) : StringRef {};
3142 if (raggedKind.empty() && rhsTy) {
3143 raggedKind = getRaggedNestedLeafKind(rhsTy);
3145 if (!raggedKind.empty()) {
3146 return op.emitOpError()
3147 <<
"cannot lower nested " << raggedKind
3148 <<
" array leaf equality for array-of-POD without per-element shape witnesses";
3151 bool lhsNeedsShapeCheck = lhsTy && needsPodArrayShapeCarrier(lhsTy);
3152 bool rhsNeedsShapeCheck = rhsTy && needsPodArrayShapeCarrier(rhsTy);
3153 SmallVector<Value> lhsCompatibleConvertedValues;
3154 SmallVector<Value> rhsCompatibleConvertedValues;
3155 if (rhsTy && rhsNeedsShapeCheck && !lhsTy &&
3156 (adaptor.getLhs().empty() || adaptor.getLhs().size() == 1)) {
3158 adaptor.getLhs().empty() ? op.
getLhs() : getSingleConvertedValue(adaptor.getLhs());
3159 lhsCompatibleConvertedValues =
3160 materializeCompatiblePodArrayConvertedValues(op.getLoc(), lhsSource, rhsTy, rewriter);
3162 if (lhsTy && lhsNeedsShapeCheck && !rhsTy &&
3163 (adaptor.getRhs().empty() || adaptor.getRhs().size() == 1)) {
3165 adaptor.getRhs().empty() ? op.
getRhs() : getSingleConvertedValue(adaptor.getRhs());
3166 rhsCompatibleConvertedValues =
3167 materializeCompatiblePodArrayConvertedValues(op.getLoc(), rhsSource, lhsTy, rewriter);
3170 ArrayType shapeCheckTy = lhsTy ? lhsTy : rhsTy;
3171 bool needsShapeCheck = lhsTy ? lhsNeedsShapeCheck : rhsNeedsShapeCheck;
3172 if (lhsTy && rhsTy) {
3174 return rewriter.notifyMatchFailure(
3175 op,
"expected array-of-pod equality operands with matching rank"
3178 needsShapeCheck = lhsNeedsShapeCheck || rhsNeedsShapeCheck;
3181 if (shapeCheckTy && needsShapeCheck) {
3182 Value lhsShapeSource = getPodArrayEqualityShapeSource(
3183 lhsTy ? lhsTy : rhsTy, op.
getLhs(),
3184 lhsTy ? adaptor.getLhs() : ValueRange(lhsCompatibleConvertedValues), op.getLoc(), rewriter
3186 Value rhsShapeSource = getPodArrayEqualityShapeSource(
3187 rhsTy ? rhsTy : lhsTy, op.
getRhs(),
3188 rhsTy ? adaptor.getRhs() : ValueRange(rhsCompatibleConvertedValues), op.getLoc(), rewriter
3191 for (
size_t dim = 0, rank = shapeCheckTy.
getDimensionSizes().size(); dim < rank; ++dim) {
3192 Value dimVal = arith::ConstantOp::create(
3203 SmallVector<Value> lhsLeaves;
3204 if (!lhsTy && rhsTy && !lhsCompatibleConvertedValues.empty()) {
3205 ValueRange materializedLeavesForLhs =
3206 getConvertedPodArrayLeafValues(rhsTy, lhsCompatibleConvertedValues);
3207 llvm::append_range(lhsLeaves, materializedLeavesForLhs);
3209 lhsLeaves = collectCompatiblePodArrayEqualityLeaves(
3210 op.getLoc(), op.
getLhs(), adaptor.getLhs(), lhsTy, rhsTy, rewriter, materializedLeaves
3214 SmallVector<Value> rhsLeaves;
3215 if (!rhsTy && lhsTy && !rhsCompatibleConvertedValues.empty()) {
3216 ValueRange materializedLeavesForRhs =
3217 getConvertedPodArrayLeafValues(lhsTy, rhsCompatibleConvertedValues);
3218 llvm::append_range(rhsLeaves, materializedLeavesForRhs);
3220 rhsLeaves = collectCompatiblePodArrayEqualityLeaves(
3221 op.getLoc(), op.
getRhs(), adaptor.getRhs(), rhsTy, lhsTy, rewriter, materializedLeaves
3224 if (lhsLeaves.size() != rhsLeaves.size()) {
3225 return rewriter.notifyMatchFailure(
3226 op,
"expected array-of-pod equality operands to expand to the same number of leaves"
3230 for (
auto [lhs, rhs] : llvm::zip_equal(lhsLeaves, rhsLeaves)) {
3235 rewriter.eraseOp(op);
3262class SplitPodArrayInEmitContainmentOp :
public OpConversionPattern<constrain::EmitContainmentOp> {
3263 CompatiblePodLeafMaterializationMap &materializedLeaves;
3266 SplitPodArrayInEmitContainmentOp(
3267 TypeConverter &tyConv, MLIRContext *ctx,
3268 CompatiblePodLeafMaterializationMap &materializedLeafMap
3270 : OpConversionPattern<constrain::EmitContainmentOp>(tyConv, ctx),
3271 materializedLeaves(materializedLeafMap) {}
3273 static bool legal(constrain::EmitContainmentOp op) {
3274 return !containsSplittablePodArrayType(op->getOperandTypes()) &&
3275 getTaggedRaggedNestedLeafKind(op.
getLhs()).empty() &&
3276 getTaggedRaggedNestedLeafKind(op.
getRhs()).empty();
3280 static SmallVector<Value> collectContainmentLeaves(
3281 Location loc, Value originalOperand, ValueRange convertedValues,
3282 ConversionPatternRewriter &rewriter,
3283 CompatiblePodLeafMaterializationMap *materializedLeaves =
nullptr,
3284 std::optional<PodType> compatiblePodTy = std::nullopt
3286 if (ArrayType arrTy = splittablePodArray(originalOperand.getType())) {
3287 ValueRange leafValues = getConvertedPodArrayLeafValues(arrTy, convertedValues);
3288 if (hasZeroLeafPodArraySplit(arrTy)) {
3291 size_t leafCount = getSplitPodArrayLeafCount(arrTy);
3292 if (leafValues.size() == leafCount) {
3293 return SmallVector<Value>(leafValues.begin(), leafValues.end());
3295 return materializeCompatiblePodArrayLeafValues(loc, originalOperand, arrTy, rewriter);
3298 if (splittablePod(originalOperand.getType())) {
3299 SmallVector<Value> podLeaves;
3300 processInputOperand(loc, getSingleConvertedValue(convertedValues), podLeaves, rewriter);
3304 if (materializedLeaves && compatiblePodTy &&
3305 (convertedValues.empty() || convertedValues.size() == 1)) {
3307 convertedValues.empty() ? originalOperand : getSingleConvertedValue(convertedValues);
3308 ArrayRef<Value> leaves = getOrMaterializeCompatiblePodLeafValues(
3309 loc,
source, *compatiblePodTy, rewriter, *materializedLeaves
3311 return SmallVector<Value>(leaves.begin(), leaves.end());
3314 return SmallVector<Value>(convertedValues.begin(), convertedValues.end());
3317 LogicalResult matchAndRewrite(
3318 constrain::EmitContainmentOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3320 if (failed(rejectRaggedNestedLeafContainment(op))) {
3328 Location loc = op.getLoc();
3329 ArrayType lhsTy = op.
getLhs().getType();
3330 Type rhsTy = op.
getRhs().getType();
3334 if (
auto rhsArrTy = llvm::dyn_cast<ArrayType>(rhsTy)) {
3335 rhsRank = rhsArrTy.getDimensionSizes().size();
3337 assert(lhsRank >= rhsRank &&
"constrain.in verifier should reject higher-rank rhs arrays");
3338 size_t selectedDims = lhsRank - rhsRank;
3340 ArrayType lhsPodArrTy = splittablePodArray(lhsTy);
3341 ArrayType rhsPodArrTy = splittablePodArray(rhsTy);
3343 (lhsPodArrTy || rhsPodArrTy) &&
3344 "containment rewrite requires at least one concrete array-of-POD operand"
3347 PodType compatibleElemPodTy = lhsPodArrTy ? llvm::cast<PodType>(lhsPodArrTy.
getElementType())
3348 : llvm::cast<PodType>(rhsPodArrTy.getElementType());
3349 ArrayType compatibleLhsTy = getCompatiblePodArrayType(lhsTy, compatibleElemPodTy);
3350 ArrayType rhsArrTy = llvm::dyn_cast<ArrayType>(rhsTy);
3351 ArrayType compatibleRhsArrTy =
3352 rhsArrTy ? getCompatiblePodArrayType(rhsArrTy, compatibleElemPodTy) : ArrayType();
3354 SmallVector<Value> lhsCompatibleConvertedValues;
3356 if (!adaptor.getLhs().empty() && adaptor.getLhs().size() != 1) {
3357 return rewriter.notifyMatchFailure(
3358 op,
"expected a single converted lhs value to materialize generic containment leaves"
3362 adaptor.getLhs().empty() ? op.
getLhs() : getSingleConvertedValue(adaptor.getLhs());
3363 lhsCompatibleConvertedValues =
3364 materializeCompatiblePodArrayConvertedValues(loc, lhsSource, compatibleLhsTy, rewriter);
3367 SmallVector<Value> rhsCompatibleConvertedValues;
3368 if (compatibleRhsArrTy && !rhsPodArrTy) {
3369 if (!adaptor.getRhs().empty() && adaptor.getRhs().size() != 1) {
3370 return rewriter.notifyMatchFailure(
3371 op,
"expected a single converted rhs value to materialize generic containment leaves"
3375 adaptor.getRhs().empty() ? op.
getRhs() : getSingleConvertedValue(adaptor.getRhs());
3376 rhsCompatibleConvertedValues = materializeCompatiblePodArrayConvertedValues(
3377 loc, rhsSource, compatibleRhsArrTy, rewriter
3381 SmallVector<Value> lhsLeaves;
3382 if (!lhsCompatibleConvertedValues.empty()) {
3384 lhsLeaves, getConvertedPodArrayLeafValues(compatibleLhsTy, lhsCompatibleConvertedValues)
3387 lhsLeaves = collectContainmentLeaves(loc, op.getLhs(), adaptor.getLhs(), rewriter);
3390 SmallVector<Value> rhsLeaves;
3391 if (!rhsCompatibleConvertedValues.empty()) {
3394 getConvertedPodArrayLeafValues(compatibleRhsArrTy, rhsCompatibleConvertedValues)
3397 rhsLeaves = collectContainmentLeaves(
3398 loc, op.getRhs(), adaptor.getRhs(), rewriter, &materializedLeaves, compatibleElemPodTy
3401 if (lhsLeaves.size() != rhsLeaves.size()) {
3402 return rewriter.notifyMatchFailure(
3403 op,
"expected array-of-pod containment operands to expand to the same number of leaves"
3407 auto getShapeSource =
3408 [&loc, &rewriter](ArrayType arrTy, Value originalValue, ValueRange convertedValues) {
3409 if (Value carrier = getConvertedPodArrayShapeSource(arrTy, convertedValues)) {
3413 if (!convertedValues.empty()) {
3414 return getSingleConvertedValue(convertedValues);
3417 return materializeArrayLengthCarrier(originalValue, arrTy, loc, rewriter);
3420 Value shapeCarrier = getShapeSource(
3421 compatibleLhsTy, op.
getLhs(),
3422 lhsCompatibleConvertedValues.empty() ? adaptor.getLhs()
3423 : ValueRange(lhsCompatibleConvertedValues)
3425 Value zero = arith::ConstantOp::create(rewriter, loc, rewriter.getIndexAttr(0));
3426 Value trueVal = arith::ConstantOp::create(
3427 rewriter, loc, IntegerAttr::get(IntegerType::get(rewriter.getContext(), 1), 1)
3430 SmallVector<Value> selectedIndices;
3431 selectedIndices.reserve(selectedDims);
3432 for (
size_t dim = 0; dim < selectedDims; ++dim) {
3433 Value idx =
NonDetOp::create(rewriter, loc, IndexType::get(rewriter.getContext()));
3434 Value dimVal = arith::ConstantOp::create(
3440 arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::sge, idx, zero);
3443 Value inRange = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::slt, idx, dimLen);
3446 selectedIndices.push_back(idx);
3449 bool lhsNeedsShapeCheck = rhsArrTy && needsPodArrayShapeCarrier(compatibleLhsTy);
3450 bool rhsNeedsShapeCheck = compatibleRhsArrTy && needsPodArrayShapeCarrier(compatibleRhsArrTy);
3451 if (compatibleRhsArrTy && (lhsNeedsShapeCheck || rhsNeedsShapeCheck)) {
3452 Value rhsShapeSource = getShapeSource(
3453 compatibleRhsArrTy, op.
getRhs(),
3454 rhsCompatibleConvertedValues.empty() ? adaptor.getRhs()
3455 : ValueRange(rhsCompatibleConvertedValues)
3457 Value selectedShapeSource =
3458 selectedIndices.empty()
3461 for (
size_t dim = 0; dim < rhsRank; ++dim) {
3462 Value dimVal = arith::ConstantOp::create(
3473 if (lhsLeaves.empty() && rhsLeaves.empty()) {
3475 Value rhsShapeCarrier = getShapeSource(rhsArrTy, op.
getRhs(), adaptor.getRhs());
3476 Value selectedShape = selectedIndices.empty()
3479 rewriter, loc, getPodArrayShapeCarrierType(rhsArrTy),
3480 shapeCarrier, selectedIndices
3486 rewriter.eraseOp(op);
3490 for (
auto [lhsLeaf, rhsLeaf] : llvm::zip_equal(lhsLeaves, rhsLeaves)) {
3491 Value selectedLhs = lhsLeaf;
3492 if (
auto rhsLeafArrTy = llvm::dyn_cast<ArrayType>(rhsLeaf.getType())) {
3493 if (!selectedIndices.empty()) {
3506 rewriter.eraseOp(op);
3517static Value selectArrayLengthShapeSource(
3518 ArrayLengthOp op, ValueRange convertedArrRefs, ConversionPatternRewriter &rewriter
3520 if (Value v = getConvertedPodArrayShapeSource(op.
getArrRefType(), convertedArrRefs)) {
3527static Value tryResolveReadPodArrayShapeSource(
3528 ReadPodOp readOp,
ArrayType arrTy,
const VirtualPodValueMap &virtualPods, Location loc,
3529 RewriterBase &rewriter
3533class RejectRaggedNestedLeafArrayLengthOp :
public OpConversionPattern<ArrayLengthOp> {
3535 using OpConversionPattern<ArrayLengthOp>::OpConversionPattern;
3537 static bool legal(ArrayLengthOp op) {
3538 return getTaggedRaggedNestedLeafKind(op.
getArrRef()).empty();
3542 matchAndRewrite(ArrayLengthOp op, OneToNOpAdaptor, ConversionPatternRewriter &)
const override {
3543 StringRef raggedKind = getTaggedRaggedNestedLeafKind(op.
getArrRef());
3544 if (raggedKind.empty()) {
3547 return op.emitOpError() <<
"cannot lower nested " << raggedKind
3548 <<
" array leaf length after reading an array-of-POD element without "
3549 "per-element shape witnesses";
3554class SplitPodArrayLengthOp :
public OpConversionPattern<ArrayLengthOp> {
3556 using OpConversionPattern<ArrayLengthOp>::OpConversionPattern;
3558 static bool legal(ArrayLengthOp op) {
3559 return RejectRaggedNestedLeafArrayLengthOp::legal(op) &&
3560 (!splittablePodArray(op.
getArrRefType()) || op->hasAttr(DEFERRED_POD_ARRAY_LENGTH_ATTR));
3563 LogicalResult matchAndRewrite(
3564 ArrayLengthOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3569 if (shouldDeferPodArrayLengthToStep3(op)) {
3571 rewriter, op.getLoc(), op.
getArrRef(), getSingleConvertedValue(adaptor.getDim())
3574 deferred->setAttr(DEFERRED_POD_ARRAY_LENGTH_ATTR, UnitAttr::get(op.getContext()));
3575 rewriter.replaceOp(op, deferred.getResult());
3578 Value arrRef = selectArrayLengthShapeSource(op, adaptor.getArrRef(), rewriter);
3580 rewriter, op, arrRef, getSingleConvertedValue(adaptor.getDim())
3591struct DeferredPodArrayBacking {
3592 SmallVector<Value> leafArrays;
3596using DeferredPodArrayBackingMap = DenseMap<Value, DeferredPodArrayBacking>;
3604class PodAccessAnalysis {
3605 using RecordKey = std::pair<Value, StringAttr>;
3610 bool hasEarlierWrite =
false;
3612 bool hasForwardableWrite =
false;
3618 DenseSet<RecordKey> earlierWrites;
3620 DenseMap<Value, WritePodOp> forwardableWrites;
3626 DenseSet<Value> usedPodValues;
3628 DenseSet<RecordKey> writtenRecords;
3631 DenseMap<Operation *, ReadFacts> readFacts;
3632 DenseMap<Operation *, DenseSet<RecordKey>> loopWrittenRecords;
3634 static void mergeInto(OpSummary &dst,
const OpSummary &src) {
3635 dst.usedPodValues.insert(src.usedPodValues.begin(), src.usedPodValues.end());
3636 dst.writtenRecords.insert(src.writtenRecords.begin(), src.writtenRecords.end());
3639 OpSummary analyzeBlock(Block &block, BlockState state) {
3640 OpSummary blockSummary;
3641 for (Operation &op : block) {
3642 if (
auto read = dyn_cast<ReadPodOp>(op)) {
3643 RecordKey key {read.getPodRef(), read.getRecordNameAttr()};
3644 auto writeIt = state.forwardableWrites.find(read.getPodRef());
3645 readFacts[read.getOperation()] = {
3646 state.earlierWrites.contains(key),
3647 writeIt != state.forwardableWrites.end() &&
3648 isSamePodRecord(writeIt->second, key.first, key.second)
3652 OpSummary opSummary;
3653 for (Value operand : op.getOperands()) {
3654 if (llvm::isa<PodType>(operand.getType())) {
3655 opSummary.usedPodValues.insert(operand);
3659 for (Region ®ion : op.getRegions()) {
3660 BlockState nestedState = state;
3661 if (isa<scf::ForOp, scf::WhileOp>(op)) {
3662 for (
const RecordKey &key : loopWrittenRecords[&op]) {
3663 auto writeIt = nestedState.forwardableWrites.find(key.first);
3664 if (writeIt != nestedState.forwardableWrites.end() &&
3665 isSamePodRecord(writeIt->second, key.first, key.second)) {
3666 nestedState.forwardableWrites.erase(writeIt);
3670 for (Block &nestedBlock : region) {
3671 mergeInto(opSummary, analyzeBlock(nestedBlock, nestedState));
3675 if (
auto write = dyn_cast<WritePodOp>(op)) {
3676 opSummary.writtenRecords.insert({write.getPodRef(), write.getRecordNameAttr()});
3679 state.earlierWrites.insert(opSummary.writtenRecords.begin(), opSummary.writtenRecords.end());
3683 if (!isa<ReadPodOp>(op)) {
3684 for (Value podRef : opSummary.usedPodValues) {
3685 state.forwardableWrites.erase(podRef);
3687 if (
auto write = dyn_cast<WritePodOp>(op)) {
3688 state.forwardableWrites[write.getPodRef()] = write;
3692 mergeInto(blockSummary, opSummary);
3694 return blockSummary;
3698 explicit PodAccessAnalysis(ModuleOp module) {
3699 module.walk([this](WritePodOp write) {
3700 RecordKey key {write.getPodRef(), write.getRecordNameAttr()};
3701 for (Operation *parent = write->getParentOp(); parent; parent = parent->getParentOp()) {
3702 if (isa<scf::ForOp, scf::WhileOp>(parent)) {
3703 loopWrittenRecords[parent].insert(key);
3707 for (Region ®ion : module->getRegions()) {
3708 for (Block &block : region) {
3709 analyzeBlock(block, BlockState());
3714 bool hasEarlierWrite(
ReadPodOp read)
const {
3715 auto it = readFacts.find(read.getOperation());
3716 return it != readFacts.end() && it->second.hasEarlierWrite;
3719 bool hasFacts(
ReadPodOp read)
const {
return readFacts.contains(read.getOperation()); }
3721 bool hasForwardableWrite(
ReadPodOp read)
const {
3722 auto it = readFacts.find(read.getOperation());
3723 return it != readFacts.end() && it->second.hasForwardableWrite;
3728static void setDeferredPodArrayBackingInsertionPoint(
ReadPodOp readOp, OpBuilder &bldr) {
3730 bldr.setInsertionPoint(loopOp);
3732 bldr.setInsertionPointAfter(readOp);
3737static DeferredPodArrayBacking &materializeDeferredPodArrayBacking(
3739 DeferredPodArrayBackingMap &deferredPodArrays, Location loc, OpBuilder &bldr,
3740 bool requireLeafArrays,
bool requireShapeCarrier
3742 auto [it, inserted] = deferredPodArrays.try_emplace(readOp.
getResult());
3743 DeferredPodArrayBacking &backing = it->second;
3745 bool needsShapeCarrier = requireShapeCarrier && needsPodArrayShapeCarrier(arrTy);
3746 bool missingLeafArrays = requireLeafArrays && backing.leafArrays.empty();
3747 bool missingShapeCarrier = needsShapeCarrier && !backing.shapeCarrier;
3748 if (missingLeafArrays || missingShapeCarrier) {
3749 OpBuilder::InsertionGuard guard(bldr);
3750 setDeferredPodArrayBackingInsertionPoint(readOp, bldr);
3752 if (missingLeafArrays) {
3753 backing.leafArrays.reserve(splitTypes.size());
3754 for (Type splitType : splitTypes) {
3755 backing.leafArrays.push_back(
3756 createWritableArrayValue(bldr, loc, llvm::cast<ArrayType>(splitType))
3761 if (missingShapeCarrier) {
3762 backing.shapeCarrier = materializeArrayLengthCarrier(readOp.
getResult(), arrTy, loc, bldr);
3766 if (inserted || requireLeafArrays) {
3768 backing.leafArrays.size() == splitTypes.size() &&
3769 "cached split POD arrays must match the rewritten read arity"
3776struct Step3Resolver {
3777 VirtualPodValueMap virtualPods;
3778 CompatiblePodLeafMaterializationMap materializedLeaves;
3779 DeferredPodArrayBackingMap deferredPodArrays;
3780 std::optional<PodAccessAnalysis> podAccesses;
3782 void rehydrateVirtualPodPlaceholders(ModuleOp modOp);
3783 void addPreConversionPatterns(RewritePatternSet &patterns);
3784 void addConversionPatterns(
3785 RewritePatternSet &patterns, SymbolTableCollection &symTables,
3786 const MemberReplacementMap &memberRepMap
3788 void addLateResolutionPatterns(RewritePatternSet &patterns);
3789 void addPostConversionPatterns(RewritePatternSet &patterns);
3790 void configureLateVirtualPodLegality(ConversionTarget &target)
const;
3791 void refreshPodAccessAnalysis(ModuleOp modOp) { podAccesses.emplace(modOp); }
3792 bool canResolveVirtualPodReadFromAnalysis(ReadPodOp op)
const;
3793 bool hasResolvableLateVirtualPodOps(ModuleOp modOp);
3794 void materializeRemainingVirtualPods(ModuleOp modOp);
3798class ResolvePodReadBackedArrayLengthOp final :
public OpConversionPattern<ArrayLengthOp> {
3799 Step3Resolver &resolver;
3802 ResolvePodReadBackedArrayLengthOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
3803 : OpConversionPattern<ArrayLengthOp>(ctx), resolver(step3Resolver) {}
3805 static bool legal(ArrayLengthOp op) {
return !op->hasAttr(DEFERRED_POD_ARRAY_LENGTH_ATTR); }
3808 matchAndRewrite(ArrayLengthOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
3810 if (!arrTy || !shouldDeferPodArrayLengthToStep3(op)) {
3815 ReadPodOp readOp = getReadPodBacking(op.
getArrRef());
3816 assert(readOp &&
"deferred POD-backed array.len must still trace to pod.read");
3817 shapeSource = tryResolveReadPodArrayShapeSource(
3818 readOp, arrTy, resolver.virtualPods, op.getLoc(), rewriter
3822 if (needsPodArrayShapeCarrier(arrTy) && isFreshUnwrittenPodRead(readOp)) {
3824 materializeDeferredPodArrayBacking(
3825 readOp, arrTy, ArrayRef<Type> {}, resolver.deferredPodArrays,
3826 op.getLoc(), rewriter,
false,
3831 auto it = resolver.deferredPodArrays.find(readOp.
getResult());
3832 it != resolver.deferredPodArrays.end() && it->second.shapeCarrier
3834 shapeSource = castValueToTypeIfNeeded(
3835 rewriter, op.getLoc(), it->second.shapeCarrier, getPodArrayShapeCarrierType(arrTy)
3841 shapeSource = materializeArrayLengthCarrier(op.
getArrRef(), arrTy, op.getLoc(), rewriter);
3845 op, rewriter.replaceOpWithNewOp<ArrayLengthOp>(op, shapeSource, op.
getDim()),
3846 DEFERRED_POD_ARRAY_LENGTH_ATTR
3853class SplitPodArrayExtractArrayOp :
public OpConversionPattern<ExtractArrayOp> {
3855 using OpConversionPattern<ExtractArrayOp>::OpConversionPattern;
3857 static bool legal(ExtractArrayOp op) {
return !splittablePodArray(op.
getResult().getType()); }
3859 LogicalResult matchAndRewrite(
3860 ExtractArrayOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3866 SmallVector<Type> splitResultTypes;
3867 splitPodArrayTypeTo(op.
getResult().getType(), splitResultTypes);
3868 if (splitResultTypes.empty()) {
3869 ArrayType resultTy = llvm::cast<ArrayType>(op.
getResult().getType());
3871 rewriter, op, getPodArrayShapeCarrierType(resultTy),
3872 getSingleConvertedValue(adaptor.getArrRef()), flattenConvertedValues(adaptor.getIndices())
3877 SmallVector<Value> indices = flattenConvertedValues(adaptor.getIndices());
3878 SmallVector<Value> replacements;
3879 ArrayType resultTy = llvm::cast<ArrayType>(op.
getResult().getType());
3880 replacements.reserve(splitResultTypes.size() + (needsPodArrayShapeCarrier(resultTy) ? 1 : 0));
3881 auto splitArrRefs = adaptor.getArrRef().take_front(splitResultTypes.size());
3882 for (
auto [splitArrRange, splitResultType] : llvm::zip_equal(splitArrRefs, splitResultTypes)) {
3885 rewriter, op.getLoc(), llvm::cast<ArrayType>(splitResultType),
3886 getSingleConvertedValue(splitArrRange), indices
3890 if (needsPodArrayShapeCarrier(resultTy)) {
3891 Value shapeCarrier = materializeExtractedPodArrayShapeCarrier(
3892 op, resultTy, op.
getArrRef(), adaptor.getArrRef(), indices, rewriter
3895 replacements.push_back(shapeCarrier);
3898 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
3904class SplitPodArrayInsertArrayOp :
public OpConversionPattern<InsertArrayOp> {
3906 using OpConversionPattern<InsertArrayOp>::OpConversionPattern;
3908 static bool legal(InsertArrayOp op) {
return !splittablePodArray(op.
getRvalue().getType()); }
3910 LogicalResult matchAndRewrite(
3911 InsertArrayOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3917 if (hasZeroLeafPodArraySplit(llvm::cast<ArrayType>(op.
getRvalue().getType()))) {
3920 rewriter, op.getLoc(), getSingleConvertedValue(adaptor.getArrRef()),
3921 flattenConvertedValues(adaptor.getIndices()),
3922 getSingleConvertedValue(adaptor.getRvalue())
3925 rewriter.eraseOp(op);
3929 ArrayType destArrTy = llvm::cast<ArrayType>(op.
getArrRef().getType());
3930 ArrayType rvalueTy = llvm::cast<ArrayType>(op.
getRvalue().getType());
3931 SmallVector<Value> indices = flattenConvertedValues(adaptor.getIndices());
3932 size_t leafCount = getSplitPodArrayLeafCount(rvalueTy);
3933 auto splitArrRefs = adaptor.getArrRef().take_front(leafCount);
3934 auto splitRvalues = adaptor.getRvalue().take_front(leafCount);
3935 for (
auto [splitArrRange, splitRvalueRange] : llvm::zip_equal(splitArrRefs, splitRvalues)) {
3938 rewriter, op.getLoc(), getSingleConvertedValue(splitArrRange), indices,
3939 getSingleConvertedValue(splitRvalueRange)
3943 if (needsPodArrayShapeCarrier(destArrTy)) {
3944 Value destCarrier = getConvertedPodArrayShapeCarrierIfPresent(destArrTy, adaptor.getArrRef());
3946 return rewriter.notifyMatchFailure(
3947 op,
"expected converted destination shape carrier for array-of-pod insert"
3951 Value rvalueCarrier =
3952 getConvertedPodArrayShapeCarrierIfPresent(rvalueTy, adaptor.getRvalue());
3953 if (!rvalueCarrier) {
3955 materializeArrayLengthCarrier(op.
getRvalue(), rvalueTy, op.getLoc(), rewriter);
3963 rewriter.eraseOp(op);
3969class SplitPodArrayInMemberWriteOp :
public OpConversionPattern<MemberWriteOp> {
3970 SymbolTableCollection &tables;
3971 const MemberReplacementMap &repMapRef;
3974 SplitPodArrayInMemberWriteOp(
3975 const TypeConverter &converter, MLIRContext *ctx, SymbolTableCollection &symTables,
3976 const MemberReplacementMap &memberRepMap
3978 : OpConversionPattern<MemberWriteOp>(converter, ctx), tables(symTables),
3979 repMapRef(memberRepMap) {}
3981 static bool legal(MemberWriteOp op) {
return !splittablePodArray(op.
getVal().getType()); }
3983 LogicalResult matchAndRewrite(
3984 MemberWriteOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
3989 StructType tgtStructTy = llvm::cast<MemberRefOpInterface>(op.getOperation()).getStructType();
3991 assert(succeeded(tgtStructDef));
3993 const LocalMemberReplacementMap &idToMember =
3995 ArrayType arrTy = llvm::cast<ArrayType>(op.
getVal().getType());
3996 SmallVector<RecordChain> splitIds;
3997 SmallVector<Type> splitTypes;
3998 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
3999 if (splitTypes.empty()) {
4000 const MemberInfo &carrierMember = idToMember.at(RecordChain());
4004 rewriter, op.getLoc(), getSingleConvertedValue(adaptor.getComponent()),
4005 FlatSymbolRefAttr::get(carrierMember.first), getSingleConvertedValue(adaptor.getVal())
4008 rewriter.eraseOp(op);
4012 auto splitVals = adaptor.getVal().take_front(splitIds.size());
4013 for (
auto [
id, splitValRange] : llvm::zip_equal(splitIds, splitVals)) {
4014 const MemberInfo &newMember = idToMember.at(
id);
4017 rewriter, op.getLoc(), getSingleConvertedValue(adaptor.getComponent()),
4018 FlatSymbolRefAttr::get(newMember.first), getSingleConvertedValue(splitValRange)
4022 if (needsPodArrayShapeCarrier(arrTy)) {
4023 Value carrier = getConvertedPodArrayShapeCarrierIfPresent(arrTy, adaptor.getVal());
4025 carrier = materializeArrayLengthCarrier(op.
getVal(), arrTy, op.getLoc(), rewriter);
4027 const MemberInfo &carrierMember = idToMember.at(RecordChain());
4030 rewriter, op.getLoc(), getSingleConvertedValue(adaptor.getComponent()),
4031 FlatSymbolRefAttr::get(carrierMember.first),
4032 castValueToTypeIfNeeded(rewriter, op.getLoc(), carrier, carrierMember.second)
4036 rewriter.eraseOp(op);
4042class SplitPodArrayInMemberReadOp :
public OpConversionPattern<MemberReadOp> {
4043 SymbolTableCollection &tables;
4044 const MemberReplacementMap &repMapRef;
4047 SplitPodArrayInMemberReadOp(
4048 const TypeConverter &converter, MLIRContext *ctx, SymbolTableCollection &symTables,
4049 const MemberReplacementMap &memberRepMap
4051 : OpConversionPattern<MemberReadOp>(converter, ctx), tables(symTables),
4052 repMapRef(memberRepMap) {}
4054 static bool legal(MemberReadOp op) {
return !splittablePodArray(op.getResult().getType()); }
4056 LogicalResult matchAndRewrite(
4057 MemberReadOp op, OneToNOpAdaptor adaptor, ConversionPatternRewriter &rewriter
4062 StructType tgtStructTy = llvm::cast<MemberRefOpInterface>(op.getOperation()).getStructType();
4064 assert(succeeded(tgtStructDef));
4066 const LocalMemberReplacementMap &idToMember =
4068 ArrayType arrTy = llvm::cast<ArrayType>(op.getType());
4069 SmallVector<RecordChain> splitIds;
4070 SmallVector<Type> splitTypes;
4071 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
4072 SmallVector<Value> mapOperands;
4073 std::optional<int32_t> numDimsPerMap;
4074 auto mapOperandsOld = adaptor.getMapOperands();
4075 if (!mapOperandsOld.empty()) {
4077 mapOperandsOld.size() == 1 &&
4078 "member.readm should have at most one affine-map operand group"
4080 mapOperands = flattenConvertedValues(mapOperandsOld.front());
4083 if (!numDimsPerMapOld.empty()) {
4085 numDimsPerMapOld.size() == 1 &&
4086 "member.readm should have one numDims entry per affine-map group"
4088 numDimsPerMap = numDimsPerMapOld.front();
4091 if (splitTypes.empty()) {
4092 const MemberInfo &carrierMember = idToMember.at(RecordChain());
4095 rewriter, op.getLoc(), carrierMember.second,
4096 getSingleConvertedValue(adaptor.getComponent()), carrierMember.first,
4097 op.
getTableOffset().value_or(
nullptr), mapOperands, numDimsPerMap
4100 rewriter.replaceOpWithMultiple(op, {ValueRange {carrierRead}});
4103 SmallVector<Value> replacements;
4104 replacements.reserve(splitIds.size() + (needsPodArrayShapeCarrier(arrTy) ? 1 : 0));
4105 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
4106 const MemberInfo &newMember = idToMember.at(
id);
4109 rewriter, op.getLoc(), splitType, getSingleConvertedValue(adaptor.getComponent()),
4110 newMember.first, op.
getTableOffset().value_or(
nullptr), mapOperands, numDimsPerMap
4114 if (needsPodArrayShapeCarrier(arrTy)) {
4115 const MemberInfo &carrierMember = idToMember.at(RecordChain());
4118 rewriter, op.getLoc(), carrierMember.second,
4119 getSingleConvertedValue(adaptor.getComponent()), carrierMember.first,
4120 op.
getTableOffset().value_or(
nullptr), mapOperands, numDimsPerMap
4124 rewriter.replaceOpWithMultiple(op, {ValueRange(replacements)});
4132static LogicalResult rejectUnsupportedPodArrayUnifiableCasts(ModuleOp modOp) {
4135 ArrayType resultArrTy = splittablePodArray(op.getType());
4136 if (!inputArrTy && !resultArrTy) {
4137 return WalkResult::advance();
4139 if (!inputArrTy && resultArrTy) {
4140 return op.emitOpError(
4141 "cannot lower a generic input to a split array-of-POD result without rewriting "
4142 "the surrounding function/template signature"
4145 if (inputArrTy && !resultArrTy) {
4146 return op.emitOpError(
4147 "cannot lower a split array-of-POD input to a non-array result without materializing "
4148 "the aggregate or rewriting the surrounding function/template signature"
4151 return WalkResult::advance();
4153 return failure(result.wasInterrupted());
4158step2(ModuleOp modOp, SymbolTableCollection &symTables,
const MemberReplacementMap &memberRepMap) {
4159 if (failed(rejectUnsupportedPodArrayUnifiableCasts(modOp))) {
4163 MLIRContext *ctx = modOp.getContext();
4164 PodArrayTypeConverter typeConverter;
4165 CompatiblePodLeafMaterializationMap materializedLeaves;
4167 RewritePatternSet patterns(ctx);
4169 SplitPodArrayNonDetOp, SplitPodArrayCreateArrayOp, SplitPodArrayReadArrayOp,
4170 SplitPodArrayWriteArrayOp, SplitPodArrayExtractArrayOp, SplitPodArrayInsertArrayOp,
4171 SplitPodArrayInFuncDefOp, SplitPodArrayInUnifiableCastOp, SplitPodArrayInReturnOp,
4172 SplitPodArrayInCallOp, RejectRaggedNestedLeafArrayLengthOp, SplitPodArrayLengthOp>(
4175 patterns.add<SplitPodArrayInEmitEqualityOp, SplitPodArrayInEmitContainmentOp>(
4176 typeConverter, ctx, materializedLeaves
4178 patterns.add<SplitPodArrayInMemberWriteOp, SplitPodArrayInMemberReadOp>(
4179 typeConverter, ctx, symTables, memberRepMap
4182 ConversionTarget target(*ctx);
4183 baseTargetSetup(target);
4184 target.addLegalOp<UnrealizedConversionCastOp>();
4185 target.addDynamicallyLegalOp<
NonDetOp>(SplitPodArrayNonDetOp::legal);
4186 target.addDynamicallyLegalOp<
CreateArrayOp>(SplitPodArrayCreateArrayOp::legal);
4187 target.addDynamicallyLegalOp<
ReadArrayOp>(SplitPodArrayReadArrayOp::legal);
4188 target.addDynamicallyLegalOp<
WriteArrayOp>(SplitPodArrayWriteArrayOp::legal);
4189 target.addDynamicallyLegalOp<
ExtractArrayOp>(SplitPodArrayExtractArrayOp::legal);
4190 target.addDynamicallyLegalOp<
InsertArrayOp>(SplitPodArrayInsertArrayOp::legal);
4191 target.addDynamicallyLegalOp<
FuncDefOp>(SplitPodArrayInFuncDefOp::legal);
4192 target.addDynamicallyLegalOp<
UnifiableCastOp>(SplitPodArrayInUnifiableCastOp::legal);
4193 target.addDynamicallyLegalOp<
ReturnOp>(SplitPodArrayInReturnOp::legal);
4194 target.addDynamicallyLegalOp<
CallOp>(SplitPodArrayInCallOp::legal);
4197 SplitPodArrayInEmitContainmentOp::legal
4199 target.addDynamicallyLegalOp<
ArrayLengthOp>(SplitPodArrayLengthOp::legal);
4200 target.addDynamicallyLegalOp<
MemberWriteOp>(SplitPodArrayInMemberWriteOp::legal);
4201 target.addDynamicallyLegalOp<
MemberReadOp>(SplitPodArrayInMemberReadOp::legal);
4203 scf::populateSCFStructuralTypeConversionsAndLegality(typeConverter, patterns, target);
4205 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 2: split arrays with POD element type\n";);
4206 return applyPartialConversion(modOp, target, std::move(patterns));
4210class SplitInitFromNewPodOp :
public OpConversionPattern<NewPodOp> {
4212 using OpConversionPattern<NewPodOp>::OpConversionPattern;
4216 LogicalResult matchAndRewrite(
4217 NewPodOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4223 rewriter.setInsertionPointAfter(op);
4224 Location loc = op.getLoc();
4225 for (
auto [name, init] :
4226 llvm::zip_equal(adaptor.getInitializedRecords(), adaptor.getInitialValues())) {
4231 rewriter.modifyOpInPlace(op, [&op]() {
4245class SplitPodElementCreateArrayOp :
public OpConversionPattern<CreateArrayOp> {
4246 const Step3Resolver &resolver;
4249 SplitPodElementCreateArrayOp(MLIRContext *ctx,
const Step3Resolver &step3Resolver)
4250 : OpConversionPattern<CreateArrayOp>(ctx), resolver(step3Resolver) {}
4252 static bool legal(CreateArrayOp op) {
4253 return !llvm::any_of(op.
getElements().getTypes(), [](Type type) {
4254 return splittablePod(type) || llvm::isa<ArrayType>(type);
4258 LogicalResult matchAndRewrite(
4259 CreateArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4264 SmallVector<Value> leafElements;
4265 leafElements.reserve(adaptor.getElements().size());
4268 for (Value element : adaptor.getElements()) {
4269 SmallVector<Value> flattenedValues;
4270 if (splittablePod(element.getType())) {
4271 processInputOperand(
4272 op.getLoc(), element, flattenedValues, rewriter, op.getOperation(),
4273 &resolver.virtualPods
4276 flattenedValues.push_back(element);
4280 flattenedValues.size() == 1 &&
4281 "array.new elements should already have been split to a single flattened leaf"
4284 leafType = flattenedValues.front().getType();
4287 leafType == flattenedValues.front().getType() &&
"array.new elements must stay uniform"
4290 leafElements.push_back(flattenedValues.front());
4293 size_t leafRank = 0;
4294 if (
auto leafArrTy = llvm::dyn_cast_if_present<ArrayType>(leafType)) {
4295 leafRank = leafArrTy.getDimensionSizes().size();
4297 ArrayType arrTy = op.getType();
4299 arrTy.
getDimensionSizes().size() >= leafRank &&
"flattened leaf rank exceeds array rank"
4302 assert(outerRank > 0 &&
"array.new elements must populate at least one outer array dimension");
4304 ArrayType outerIndexTy =
4308 elementIndices &&
"array.new with explicit POD elements requires static outer dimensions"
4311 elementIndices->size() == leafElements.size() &&
4312 "array.new element count must match the outer array cardinality"
4315 Value rebuiltArray = createWritableArrayValue(rewriter, op.getLoc(), arrTy);
4317 for (
auto [index, leafValue] : llvm::zip_equal(*elementIndices, leafElements)) {
4318 genArrayWrite(rewriter, op.getLoc(), rebuiltArray, index, leafValue);
4320 rewriter.replaceOp(op, rebuiltArray);
4332class SplitPodInFuncDefOp :
public OpConversionPattern<FuncDefOp> {
4333 Step3Resolver &resolver;
4336 SplitPodInFuncDefOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
4337 : OpConversionPattern<FuncDefOp>(ctx), resolver(step3Resolver) {}
4339 inline static bool legal(FuncDefOp op) {
4345 matchAndRewrite(FuncDefOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
4350 class Impl :
public FunctionTypeConverter {
4351 SmallVector<size_t> originalInputIdxToSize, originalResultIdxToSize;
4352 SplitFunctionNameInfo inputNameInfo;
4353 SplitFunctionNameInfo resultNameInfo;
4354 Step3Resolver &resolver;
4357 SmallVector<Type> convertInputs(ArrayRef<Type> origTypes)
override {
4358 return splitPodType(origTypes, &originalInputIdxToSize);
4360 SmallVector<Type> convertResults(ArrayRef<Type> origTypes)
override {
4361 return splitPodType(origTypes, &originalResultIdxToSize);
4363 ArrayAttr convertInputAttrs(ArrayAttr origAttrs, SmallVector<Type> newTypes)
override {
4370 ArrayAttr convertResultAttrs(ArrayAttr origAttrs, SmallVector<Type> newTypes)
override {
4382 void processBlockArgs(Block &entryBlock, RewriterBase &rewriter)
override {
4383 OpBuilder::InsertionGuard guard(rewriter);
4384 rewriter.setInsertionPointToStart(&entryBlock);
4386 for (
unsigned i = 0; i < entryBlock.getNumArguments();) {
4387 Value oldV = entryBlock.getArgument(i);
4388 if (PodType pt = splittablePod(oldV.getType())) {
4389 Location loc = oldV.getLoc();
4390 VirtualPodLeafMap leafValues;
4391 SmallVector<StringAttr> recordChain;
4392 unsigned nextArgIdx = i + 1;
4393 forEachPodLeaf(pt, recordChain, [&](
const RecordChain &
id, Type leafType) {
4394 BlockArgument newArg = entryBlock.insertArgument(nextArgIdx, leafType, loc);
4395 leafValues[id] = newArg;
4399 Value virtualPod = createVirtualPodPlaceholder(rewriter, loc, pt, leafValues);
4404 oldV.replaceAllUsesWith(virtualPod);
4405 entryBlock.eraseArgument(i);
4407 i += leafValues.size();
4408 resolver.virtualPods[virtualPod] = std::move(leafValues);
4416 Impl(FuncDefOp op, Step3Resolver &step3Resolver) : resolver(step3Resolver) {
4418 return op.getArgNameAttr(i);
4419 }, getSplitRecordNameSuffixes);
4421 op.
getResultTypes(), [resultAttrs = op.getAllResultAttrs()](
unsigned i) {
4422 return getAttrAtIndexWithName(resultAttrs, i, RES_NAME_ATTR_NAME);
4423 }, getSplitRecordNameSuffixes
4427 Impl(op, resolver).convert(op, rewriter);
4437class SplitPodInReturnOp :
public OpConversionPattern<ReturnOp> {
4438 const Step3Resolver &resolver;
4441 SplitPodInReturnOp(MLIRContext *ctx,
const Step3Resolver &step3Resolver)
4442 : OpConversionPattern<ReturnOp>(ctx), resolver(step3Resolver) {}
4444 inline static bool legal(ReturnOp op) {
4445 return !containsSplittablePodType(op.
getOperands().getTypes());
4448 LogicalResult matchAndRewrite(
4449 ReturnOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4454 processInputOperands(
4462static CallOp newCallOpWithSplitResults(
4464 Step3Resolver &resolver
4466 OpBuilder::InsertionGuard guard(rewriter);
4467 rewriter.setInsertionPointAfter(oldCall);
4469 Operation::result_range oldResults = oldCall.getResults();
4471 oldCall.getLoc(), splitPodType(oldResults.getTypes()), oldCall, adaptor.
getMapOperands(),
4475 auto newResults = newCall.getResults().begin();
4476 SmallVector<Value> replacements;
4477 replacements.reserve(oldResults.size());
4478 for (Value oldVal : oldResults) {
4479 if (
PodType pt = splittablePod(oldVal.getType())) {
4480 Location loc = oldVal.getLoc();
4481 VirtualPodLeafMap leafValues;
4482 SmallVector<StringAttr> recordChain;
4483 forEachPodLeaf(pt, recordChain, [&leafValues, &newResults](
const RecordChain &
id, Type) {
4484 leafValues[id] = *newResults;
4487 Value virtualPod = createVirtualPodPlaceholder(rewriter, loc, pt, leafValues);
4488 resolver.virtualPods[virtualPod] = std::move(leafValues);
4489 replacements.push_back(virtualPod);
4491 replacements.push_back(*newResults);
4497 rewriter.replaceOp(oldCall, replacements);
4508class SplitPodInCallOp :
public OpConversionPattern<CallOp> {
4509 Step3Resolver &resolver;
4512 SplitPodInCallOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
4513 : OpConversionPattern<CallOp>(ctx), resolver(step3Resolver) {}
4515 inline static bool legal(CallOp op) {
4516 return !containsSplittablePodType(op.
getArgOperands().getTypes()) &&
4517 !containsSplittablePodType(op.getResultTypes());
4520 LogicalResult matchAndRewrite(
4521 CallOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4527 CallOp newCall = newCallOpWithSplitResults(op, adaptor, rewriter, resolver);
4528 processInputOperands(
4530 &resolver.virtualPods
4537class SplitPodInMemberWriteOp :
public OpConversionPattern<MemberWriteOp> {
4538 SymbolTableCollection &tables;
4539 const MemberReplacementMap &repMapRef;
4540 const Step3Resolver &resolver;
4543 SplitPodInMemberWriteOp(
4544 MLIRContext *ctx, SymbolTableCollection &symTables,
const MemberReplacementMap &memberRepMap,
4545 const Step3Resolver &step3Resolver
4547 : OpConversionPattern<MemberWriteOp>(ctx), tables(symTables), repMapRef(memberRepMap),
4548 resolver(step3Resolver) {}
4550 static bool legal(MemberWriteOp op) {
return !containsSplittablePodType(op.
getVal().getType()); }
4552 LogicalResult matchAndRewrite(
4553 MemberWriteOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4558 StructType tgtStructTy = llvm::cast<MemberRefOpInterface>(op.getOperation()).getStructType();
4560 assert(succeeded(tgtStructDef));
4562 const LocalMemberReplacementMap &idToMember =
4564 const VirtualPodLeafMap *virtualLeafValues =
4565 !hasEarlierWriteToPod(op.getOperation(), op.
getVal())
4566 ? lookupVirtualPodLeafMap(op.
getVal(), resolver.virtualPods)
4569 for (
const auto &[
id, newMember] : idToMember) {
4570 Value scalarValue = virtualLeafValues
4571 ? virtualLeafValues->at(
id)
4572 : genReadAlongPath(rewriter, op.getLoc(), op.
getVal(),
id);
4576 FlatSymbolRefAttr::get(newMember.first), scalarValue
4580 rewriter.eraseOp(op);
4586class SplitPodInMemberReadOp :
public OpConversionPattern<MemberReadOp> {
4587 SymbolTableCollection &tables;
4588 const MemberReplacementMap &repMapRef;
4589 Step3Resolver &resolver;
4592 SplitPodInMemberReadOp(
4593 MLIRContext *ctx, SymbolTableCollection &symTables,
const MemberReplacementMap &memberRepMap,
4594 Step3Resolver &step3Resolver
4596 : OpConversionPattern<MemberReadOp>(ctx), tables(symTables), repMapRef(memberRepMap),
4597 resolver(step3Resolver) {}
4599 static bool legal(MemberReadOp op) {
4600 return !containsSplittablePodType(op.getResult().getType());
4603 LogicalResult matchAndRewrite(
4604 MemberReadOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4609 StructType tgtStructTy = llvm::cast<MemberRefOpInterface>(op.getOperation()).getStructType();
4611 assert(succeeded(tgtStructDef));
4613 const LocalMemberReplacementMap &idToMember =
4616 VirtualPodLeafMap leafValues;
4617 for (
const auto &[
id, newMember] : idToMember) {
4620 rewriter, op.getLoc(), newMember.second, adaptor.
getComponent(), newMember.first
4625 PodType podTy = llvm::cast<PodType>(op.getType());
4626 Value virtualPod = createVirtualPodPlaceholder(rewriter, op.getLoc(), podTy, leafValues);
4627 resolver.virtualPods[virtualPod] = std::move(leafValues);
4628 rewriter.replaceOp(op, virtualPod);
4635static bool tryCollectReadPodSplitPodArrayLeafValues(
4637 const VirtualPodValueMap &virtualPods, SmallVectorImpl<Value> &leafArrays
4639 if (hasEarlierWrite(readOp) || findNearestForwardableWrite(readOp)) {
4642 const VirtualPodLeafMap *podLeafValues = lookupVirtualPodLeafMap(readOp.
getPodRef(), virtualPods);
4643 if (!podLeafValues) {
4646 SmallVector<Value> stagedLeafArrays;
4647 for (
const RecordChain &
id : splitIds) {
4649 if (it == podLeafValues->end() ||
4650 !
typesUnify(it->second.getType(), getFlattenedTypeAlongPath(arrTy,
id)) ||
4654 stagedLeafArrays.push_back(it->second);
4656 llvm::append_range(leafArrays, stagedLeafArrays);
4661static bool resolveReadPodSplitPodArrayLeafValues(
4662 ReadPodOp readOp,
ArrayType arrTy, ArrayRef<RecordChain> splitIds, ArrayRef<Type> splitTypes,
4663 const VirtualPodValueMap &virtualPods, DeferredPodArrayBackingMap &deferredPodArrays,
4664 Location loc, OpBuilder &bldr, SmallVectorImpl<Value> &leafArrays
4666 auto cached = deferredPodArrays.find(readOp.
getResult());
4667 if (cached != deferredPodArrays.end() && !cached->second.leafArrays.empty()) {
4668 llvm::append_range(leafArrays, cached->second.leafArrays);
4672 SmallVector<Value> sourceLeafArrays;
4673 if (tryCollectReadPodSplitPodArrayLeafValues(
4674 readOp, arrTy, splitIds, virtualPods, sourceLeafArrays
4676 OpBuilder::InsertionGuard guard(bldr);
4679 bldr.setInsertionPointAfter(readOp);
4680 DeferredPodArrayBacking &backing = deferredPodArrays[readOp.
getResult()];
4681 backing.leafArrays.reserve(sourceLeafArrays.size());
4682 for (Value
source : sourceLeafArrays) {
4684 assert(succeeded(copied) &&
"copy support was checked before materialization");
4685 backing.leafArrays.push_back(*copied);
4687 llvm::append_range(leafArrays, backing.leafArrays);
4691 if (!isFreshUnwrittenPodRead(readOp)) {
4697 DeferredPodArrayBacking &backing = materializeDeferredPodArrayBacking(
4698 readOp, arrTy, splitTypes, deferredPodArrays, loc, bldr,
4701 leafArrays.assign(backing.leafArrays.begin(), backing.leafArrays.end());
4707static Value tryResolveReadPodArrayShapeSource(
4708 ReadPodOp readOp,
ArrayType arrTy,
const VirtualPodValueMap &virtualPods, Location loc,
4709 RewriterBase &rewriter
4711 auto tryGetVirtualShapeSource = [&](
ReadPodOp sourceRead) -> Value {
4712 if (hasEarlierWrite(sourceRead)) {
4716 const VirtualPodLeafMap *podLeafValues =
4717 lookupVirtualPodLeafMap(sourceRead.getPodRef(), virtualPods);
4718 if (!podLeafValues) {
4722 SmallVector<StringAttr> carrierPath {
4723 sourceRead.getRecordNameAttr(), getPodArrayShapeCarrierMarker(rewriter.getContext())
4725 if (
auto carrierIt = podLeafValues->find(RecordChain(carrierPath,
true));
4726 carrierIt != podLeafValues->end()) {
4727 return castValueToTypeIfNeeded(
4728 rewriter, loc, carrierIt->second, getPodArrayShapeCarrierType(arrTy)
4733 SmallVector<RecordChain> splitIds;
4734 SmallVector<Type> splitTypes;
4735 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
4736 for (
auto [
id, splitType] : llvm::zip_equal(splitIds, splitTypes)) {
4737 auto splitArrTy = llvm::dyn_cast<ArrayType>(splitType);
4738 if (!splitArrTy || splitArrTy.getDimensionSizes().size() != originalRank) {
4742 auto valueIt = podLeafValues->find(
id.withPrefix({sourceRead.getRecordNameAttr()}));
4743 if (valueIt != podLeafValues->end()) {
4744 return castValueToTypeIfNeeded(rewriter, loc, valueIt->second, splitArrTy);
4751 if (!hasEarlierWrite(readOp)) {
4752 if (Value shapeSource = tryGetVirtualShapeSource(readOp)) {
4757 if (
WritePodOp writeOp = findNearestForwardableWrite(readOp)) {
4758 if (Value shapeSource = tryCollectDirectConvertedPodArrayShapeSource(
4759 writeOp.
getValue(), arrTy, loc, rewriter
4764 if (Value shapeSource = tryGetVirtualShapeSource(writtenRead)) {
4774static void eraseDeadDeferredFieldReadChain(
ReadPodOp readOp, PatternRewriter &rewriter) {
4780 rewriter.eraseOp(readOp);
4781 if (podRef.use_empty()) {
4782 if (
auto cast = podRef.getDefiningOp<UnrealizedConversionCastOp>()) {
4783 if (
cast->getNumResults() == 1 &&
cast.getResult(0) == podRef) {
4784 rewriter.eraseOp(
cast);
4791static bool getDeferredSplitPodArrayCastInfo(
4792 UnrealizedConversionCastOp op,
ArrayType &arrTy, SmallVector<RecordChain> &splitIds,
4793 SmallVectorImpl<Type> &splitTypes
4795 if (op->getNumOperands() != 1) {
4799 arrTy = splittablePodArray(op.getOperand(0).getType());
4804 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
4805 size_t expectedResults = splitTypes.size() + (needsPodArrayShapeCarrier(arrTy) ? 1 : 0);
4806 if (op->getNumResults() != expectedResults) {
4810 for (
auto [result, splitType] :
4811 llvm::zip_equal(op.getResults().take_front(splitTypes.size()), splitTypes)) {
4812 if (result.getType() != splitType) {
4816 return !needsPodArrayShapeCarrier(arrTy) ||
4817 op.getResult(splitTypes.size()).getType() == getPodArrayShapeCarrierType(arrTy);
4821static LogicalResult resolveDeferredSplitPodArrayCast(
4822 UnrealizedConversionCastOp op, PatternRewriter &rewriter, Step3Resolver &resolver
4825 SmallVector<RecordChain> splitIds;
4826 SmallVector<Type> splitTypes;
4827 if (!getDeferredSplitPodArrayCastInfo(op, arrTy, splitIds, splitTypes)) {
4831 ReadPodOp fieldRead = peelUnifiableCasts(op.getOperand(0)).getDefiningOp<
ReadPodOp>();
4836 SmallVector<Value> splitLeafArrays;
4837 if (!resolveReadPodSplitPodArrayLeafValues(
4838 fieldRead, arrTy, splitIds, splitTypes, resolver.virtualPods, resolver.deferredPodArrays,
4839 op.getLoc(), rewriter, splitLeafArrays
4844 SmallVector<Value> replacements(splitLeafArrays.begin(), splitLeafArrays.end());
4845 if (needsPodArrayShapeCarrier(arrTy)) {
4846 Value carrier = tryResolveReadPodArrayShapeSource(
4847 fieldRead, arrTy, resolver.virtualPods, op.getLoc(), rewriter
4850 if (
auto it = resolver.deferredPodArrays.find(fieldRead.
getResult());
4851 it != resolver.deferredPodArrays.end() && it->second.shapeCarrier) {
4852 carrier = castValueToTypeIfNeeded(
4853 rewriter, op.getLoc(), it->second.shapeCarrier, getPodArrayShapeCarrierType(arrTy)
4857 materializeArrayLengthCarrier(fieldRead.
getResult(), arrTy, op.getLoc(), rewriter);
4860 replacements.push_back(carrier);
4863 rewriter.replaceOp(op, replacements);
4864 eraseDeadDeferredFieldReadChain(fieldRead, rewriter);
4873class ResolvePodReadBackedArrayReadOp :
public OpConversionPattern<ReadArrayOp> {
4874 Step3Resolver &resolver;
4877 ResolvePodReadBackedArrayReadOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
4878 : OpConversionPattern<ReadArrayOp>(ctx), resolver(step3Resolver) {}
4880 static bool canResolve(ReadArrayOp op,
const Step3Resolver &resolver) {
4881 if (!shouldDeferPodArrayReadToStep3(op)) {
4886 auto fieldRead = llvm::cast<ReadPodOp>(op.
getArrRef().getDefiningOp());
4887 SmallVector<RecordChain> splitIds;
4888 SmallVector<Type> splitTypes;
4889 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
4891 SmallVector<Value> ignoredLeafArrays;
4892 return tryCollectReadPodSplitPodArrayLeafValues(
4893 fieldRead, arrTy, splitIds, resolver.virtualPods, ignoredLeafArrays
4895 isFreshUnwrittenPodRead(fieldRead);
4898 LogicalResult matchAndRewrite(
4899 ReadArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
4901 auto fieldRead = op.
getArrRef().getDefiningOp<ReadPodOp>();
4908 SmallVector<RecordChain> splitIds;
4909 SmallVector<Type> splitTypes;
4910 splitPodArrayTypeTo(arrTy, splitTypes, &splitIds);
4912 SmallVector<Value> splitLeafArrays;
4913 if (!resolveReadPodSplitPodArrayLeafValues(
4914 fieldRead, arrTy, splitIds, splitTypes, resolver.virtualPods,
4915 resolver.deferredPodArrays, op.getLoc(), rewriter, splitLeafArrays
4920 SmallVector<Value> indices(adaptor.getIndices().begin(), adaptor.getIndices().end());
4921 VirtualPodLeafMap leafValues;
4922 for (
auto [
id, splitType, leafArray] : llvm::zip_equal(splitIds, splitTypes, splitLeafArrays)) {
4925 leafValues[id] = tagRaggedNestedLeafValue(
4926 rewriter, op.getLoc(), leafValue, getRaggedNestedLeafAttrName(arrTy, splitType)
4930 Value virtualPod = createVirtualPodPlaceholder(rewriter, op.getLoc(), podTy, leafValues);
4931 resolver.virtualPods[virtualPod] = std::move(leafValues);
4932 rewriter.replaceOp(op, virtualPod);
4933 eraseDeadDeferredFieldReadChain(fieldRead, rewriter);
4945class ResolveDeferredSplitPodArrayCastOp :
public OpConversionPattern<UnrealizedConversionCastOp> {
4946 Step3Resolver &resolver;
4949 ResolveDeferredSplitPodArrayCastOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
4950 : OpConversionPattern<UnrealizedConversionCastOp>(ctx), resolver(step3Resolver) {}
4952 static bool canResolve(UnrealizedConversionCastOp op,
const Step3Resolver &resolver) {
4954 SmallVector<RecordChain> splitIds;
4955 SmallVector<Type> splitTypes;
4956 if (!getDeferredSplitPodArrayCastInfo(op, arrTy, splitIds, splitTypes)) {
4960 ReadPodOp fieldRead = peelUnifiableCasts(op.getOperand(0)).getDefiningOp<ReadPodOp>();
4965 SmallVector<Value> ignoredLeafArrays;
4966 if (tryCollectReadPodSplitPodArrayLeafValues(
4967 fieldRead, arrTy, splitIds, resolver.virtualPods, ignoredLeafArrays
4971 return isFreshUnwrittenPodRead(fieldRead);
4974 LogicalResult matchAndRewrite(
4975 UnrealizedConversionCastOp op, OpAdaptor, ConversionPatternRewriter &rewriter
4977 return resolveDeferredSplitPodArrayCast(op, rewriter, resolver);
4982class ResolveDeferredSplitPodArrayCastPrepass final
4983 :
public OpRewritePattern<UnrealizedConversionCastOp> {
4984 Step3Resolver &resolver;
4987 ResolveDeferredSplitPodArrayCastPrepass(MLIRContext *ctx, Step3Resolver &step3Resolver)
4988 : OpRewritePattern<UnrealizedConversionCastOp>(ctx), resolver(step3Resolver) {}
4991 matchAndRewrite(UnrealizedConversionCastOp op, PatternRewriter &rewriter)
const override {
4992 return resolveDeferredSplitPodArrayCast(op, rewriter, resolver);
4997static LogicalResult splitVirtualPodEmitEquality(
5000 return splitWholePodEmitEquality(
5001 op, rewriter, resolver.materializedLeaves, op.getOperation(), &resolver.virtualPods
5011static LogicalResult splitEarlierVirtualPodEqualitiesBeforeWriteInBlock(
5012 WritePodOp writeOp, RewriterBase &rewriter, Step3Resolver &resolver
5014 Value writtenPod = peelVirtualPodCompatibilityCasts(writeOp.
getPodRef());
5016 SmallVector<constrain::EmitEqualityOp> equalityOps;
5017 for (Operation &candidate : llvm::make_early_inc_range(*writeOp->getBlock())) {
5018 if (&candidate == writeOp) {
5022 if (getWholePodEqualityType(op)) {
5023 equalityOps.push_back(op);
5029 Value lhs = peelVirtualPodCompatibilityCasts(equalityOp.getLhs());
5030 Value rhs = peelVirtualPodCompatibilityCasts(equalityOp.getRhs());
5031 if (lhs != writtenPod && rhs != writtenPod) {
5035 if (!lookupVirtualPodLeafMap(equalityOp.getLhs(), resolver.virtualPods) &&
5036 !lookupVirtualPodLeafMap(equalityOp.getRhs(), resolver.virtualPods)) {
5040 rewriter.setInsertionPoint(equalityOp);
5041 if (failed(splitVirtualPodEmitEquality(equalityOp, rewriter, resolver))) {
5052class ResolveVirtualPodWriteOp :
public OpConversionPattern<WritePodOp> {
5053 Step3Resolver &resolver;
5056 ResolveVirtualPodWriteOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
5057 : OpConversionPattern<WritePodOp>(ctx), resolver(step3Resolver) {}
5059 LogicalResult matchAndRewrite(
5060 WritePodOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
5062 auto it = lookupVirtualPodLeafMapIt(op.
getPodRef(), resolver.virtualPods);
5063 if (it == resolver.virtualPods.end() || isInsideSupportedScfRegion(op.getOperation())) {
5067 if (failed(splitEarlierVirtualPodEqualitiesBeforeWriteInBlock(op, rewriter, resolver))) {
5073 assert(recordType &&
"record must exist in POD type");
5074 if (failed(updateVirtualPodRecordLeafValues(
5076 resolver.virtualPods, rewriter, it->second
5080 rewriter.eraseOp(op);
5089class ResolveVirtualPodReadOp :
public OpConversionPattern<ReadPodOp> {
5090 Step3Resolver &resolver;
5093 ResolveVirtualPodReadOp(MLIRContext *ctx, Step3Resolver &step3Resolver)
5094 : OpConversionPattern<ReadPodOp>(ctx), resolver(step3Resolver) {}
5097 matchAndRewrite(ReadPodOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
5098 if (!resolver.canResolveVirtualPodReadFromAnalysis(op)) {
5102 const VirtualPodLeafMap *leafValues =
5103 lookupVirtualPodLeafMap(op.
getPodRef(), resolver.virtualPods);
5111 assert(recordType &&
"record must exist in POD type");
5113 if (PodType nestedPodTy = llvm::dyn_cast<PodType>(recordType)) {
5114 VirtualPodLeafMap nestedLeafValues;
5115 SmallVector<StringAttr> nestedRecordChain;
5116 bool failedToCopy =
false;
5117 rewriter.setInsertionPoint(op);
5119 nestedPodTy, nestedRecordChain,
5120 [leafValues, &prefix, &failedToCopy, &rewriter, loc = op.getLoc(),
5121 &nestedLeafValues](
const RecordChain &
id, Type) {
5122 Value leaf = leafValues->at(id.withPrefix(prefix));
5123 FailureOr<Value> copied = leaf;
5124 if (llzk::requiresValueCopy(leaf.getType())) {
5125 copied = llzk::materializeValueCopy(rewriter, loc, leaf);
5127 if (failed(copied)) {
5128 failedToCopy =
true;
5131 nestedLeafValues[id] = *copied;
5138 createVirtualPodPlaceholder(rewriter, op.getLoc(), nestedPodTy, nestedLeafValues);
5139 resolver.virtualPods[virtualPod] = std::move(nestedLeafValues);
5140 rewriter.replaceOp(op, virtualPod);
5144 if (splittablePodArray(recordType)) {
5148 rewriter.setInsertionPoint(op);
5149 Value stored = castValueToTypeIfNeeded(
5150 rewriter, op.getLoc(), leafValues->at(RecordChain(prefix)), recordType
5152 FailureOr<Value> copied = stored;
5156 if (failed(copied)) {
5159 rewriter.replaceOp(op, *copied);
5165static void rehydrateVirtualPodPlaceholders(ModuleOp modOp, VirtualPodValueMap &virtualPods) {
5166 modOp.walk([&virtualPods](UnrealizedConversionCastOp castOp) {
5167 if (castOp->getNumResults() != 1) {
5171 PodType podTy = llvm::dyn_cast<PodType>(castOp.getResult(0).getType());
5176 VirtualPodLeafMap leafValues;
5177 SmallVector<StringAttr> recordChain;
5178 auto operandIt = castOp.getOperands().begin();
5179 bool matchesVirtualPlaceholder =
true;
5180 forEachPodLeaf(podTy, recordChain, [&](
const RecordChain &
id, Type leafType) {
5181 if (!matchesVirtualPlaceholder || operandIt == castOp.getOperands().end() ||
5182 !
typesUnify((*operandIt).getType(), leafType)) {
5183 matchesVirtualPlaceholder =
false;
5186 leafValues[id] = *operandIt++;
5189 if (matchesVirtualPlaceholder && operandIt == castOp.getOperands().end()) {
5190 virtualPods[castOp.getResult(0)] = std::move(leafValues);
5196class SplitVirtualPodInEmitEqualityPattern final
5197 :
public OpRewritePattern<constrain::EmitEqualityOp> {
5198 Step3Resolver &resolver;
5201 SplitVirtualPodInEmitEqualityPattern(MLIRContext *ctx, Step3Resolver &step3Resolver)
5202 : OpRewritePattern<constrain::EmitEqualityOp>(ctx), resolver(step3Resolver) {}
5205 matchAndRewrite(constrain::EmitEqualityOp op, PatternRewriter &rewriter)
const override {
5206 if (!getWholePodEqualityType(op)) {
5210 return splitVirtualPodEmitEquality(op, rewriter, resolver);
5221struct PromotedFunctionArgCast {
5223 SmallVector<Type> resultTypes;
5224 SmallVector<UnrealizedConversionCastOp> casts;
5232struct PromotedFunctionSignature {
5235 unsigned oldInputCount;
5236 SmallVector<PromotedFunctionArgCast> argCasts;
5239static const PromotedFunctionArgCast *
5240findPromotedArgCast(ArrayRef<PromotedFunctionArgCast> argCasts,
unsigned argIndex) {
5241 const auto *it = llvm::find_if(argCasts, [argIndex](
const PromotedFunctionArgCast &argCast) {
5242 return argCast.argIndex == argIndex;
5244 return it == argCasts.end() ? nullptr : &*it;
5253static std::optional<PromotedFunctionArgCast> getPromotableFunctionArgCast(BlockArgument arg) {
5254 PromotedFunctionArgCast promoted;
5255 promoted.argIndex = arg.getArgNumber();
5256 bool initialized =
false;
5258 for (OpOperand &
use : arg.getUses()) {
5259 auto castOp = dyn_cast<UnrealizedConversionCastOp>(
use.getOwner());
5260 if (!castOp || castOp->getNumOperands() != 1 || castOp.getOperand(0) != arg ||
5261 castOp->getNumResults() <= 1) {
5262 return std::nullopt;
5265 SmallVector<Type> castResultTypes(castOp.getResultTypes());
5267 promoted.resultTypes = std::move(castResultTypes);
5269 }
else if (promoted.resultTypes != castResultTypes) {
5270 return std::nullopt;
5272 promoted.casts.push_back(castOp);
5275 return initialized ? std::optional<PromotedFunctionArgCast>(std::move(promoted)) : std::nullopt;
5283static ArrayAttr expandArgAttrsForPromotedFunctionArgCasts(
5284 FuncDefOp func, ArrayRef<PromotedFunctionArgCast> argCasts
5291 llvm::StringSet<> usedNames;
5292 for (
auto [i, attr] : llvm::enumerate(origAttrs)) {
5293 if (findPromotedArgCast(argCasts, i)) {
5296 auto dictAttr = dyn_cast<DictionaryAttr>(attr);
5300 if (
auto nameAttr = dyn_cast_if_present<StringAttr>(dictAttr.get(
ARG_NAME_ATTR_NAME))) {
5301 usedNames.insert(nameAttr.getValue());
5305 SmallVector<Attribute> newAttrs;
5306 for (
auto [i, attr] : llvm::enumerate(origAttrs)) {
5307 const PromotedFunctionArgCast *argCast = findPromotedArgCast(argCasts, i);
5309 newAttrs.push_back(attr);
5313 auto dictAttr = llvm::cast<DictionaryAttr>(attr);
5316 newAttrs.append(argCast->resultTypes.size(), attr);
5320 llvm::StringRef baseName = nameAttr.getValue();
5321 for (
unsigned splitIdx = 0, e = argCast->resultTypes.size(); splitIdx < e; ++splitIdx) {
5322 std::string desiredName =
5323 splitIdx == 0 ? baseName.str() : (baseName +
"#" + llvm::Twine(splitIdx)).str();
5329 return ArrayAttr::get(func.getContext(), newAttrs);
5340static std::optional<PromotedFunctionSignature> promoteFunctionArgCasts(
FuncDefOp func) {
5341 if (func.isExternal()) {
5342 return std::nullopt;
5345 Block &entryBlock = func.
getBody().front();
5346 SmallVector<PromotedFunctionArgCast> argCasts;
5347 for (BlockArgument arg : entryBlock.getArguments()) {
5348 if (
auto promoted = getPromotableFunctionArgCast(arg)) {
5349 argCasts.push_back(std::move(*promoted));
5352 if (argCasts.empty()) {
5353 return std::nullopt;
5357 SmallVector<Type> newInputs;
5358 for (
auto [i, inputType] : llvm::enumerate(oldFuncTy.getInputs())) {
5359 if (
const PromotedFunctionArgCast *argCast = findPromotedArgCast(argCasts, i)) {
5360 llvm::append_range(newInputs, argCast->resultTypes);
5362 newInputs.push_back(inputType);
5366 ArrayAttr newArgAttrs = expandArgAttrsForPromotedFunctionArgCasts(func, argCasts);
5367 func.
setFunctionType(FunctionType::get(func.getContext(), newInputs, oldFuncTy.getResults()));
5372 for (
const PromotedFunctionArgCast &argCast : llvm::reverse(argCasts)) {
5373 BlockArgument oldArg = entryBlock.getArgument(argCast.argIndex);
5374 SmallVector<BlockArgument> newArgs;
5375 newArgs.reserve(argCast.resultTypes.size());
5376 unsigned nextArgIdx = argCast.argIndex + 1;
5377 for (Type resultType : argCast.resultTypes) {
5378 newArgs.push_back(entryBlock.insertArgument(nextArgIdx, resultType, oldArg.getLoc()));
5382 for (UnrealizedConversionCastOp castOp : argCast.casts) {
5383 for (
auto [result, newArg] : llvm::zip_equal(castOp.getResults(), newArgs)) {
5384 result.replaceAllUsesWith(newArg);
5388 entryBlock.eraseArgument(argCast.argIndex);
5391 return PromotedFunctionSignature {
5393 .funcOp = func.getOperation(),
5394 .oldInputCount = oldFuncTy.getNumInputs(),
5395 .argCasts = std::move(argCasts)
5405static LogicalResult updateCallsForPromotedFunctionArgCasts(
5406 ModuleOp modOp, SymbolTableCollection &symTables,
5407 ArrayRef<PromotedFunctionSignature> promotedSignatures
5409 if (promotedSignatures.empty()) {
5413 DenseMap<Operation *, const PromotedFunctionSignature *> promotedByFunc;
5414 for (
const PromotedFunctionSignature &signature : promotedSignatures) {
5415 promotedByFunc[signature.funcOp] = &signature;
5418 OpBuilder builder(modOp.getContext());
5419 for (
CallOp callOp : walkCollect<CallOp>(modOp)) {
5420 FailureOr<SymbolLookupResult<FuncDefOp>> targetRes = callOp.getCalleeTarget(symTables);
5421 if (failed(targetRes)) {
5425 auto signatureIt = promotedByFunc.find(targetRes->get().getOperation());
5426 if (signatureIt == promotedByFunc.end()) {
5430 const PromotedFunctionSignature &signature = *signatureIt->second;
5431 if (callOp.getArgOperands().size() != signature.oldInputCount) {
5432 return callOp.emitOpError(
"argument count does not match pre-promotion callee signature");
5435 SmallVector<Value> newOperands;
5436 builder.setInsertionPoint(callOp);
5437 for (
auto [i, operand] : llvm::enumerate(callOp.getArgOperands())) {
5438 if (
const PromotedFunctionArgCast *argCast = findPromotedArgCast(signature.argCasts, i)) {
5439 auto castOp = UnrealizedConversionCastOp::create(
5440 builder, callOp.getLoc(), TypeRange(argCast->resultTypes), operand
5442 llvm::append_range(newOperands, castOp.getResults());
5444 newOperands.push_back(operand);
5447 callOp.getArgOperandsMutable().assign(ValueRange(newOperands));
5458promoteFunctionArgCastsToSignature(ModuleOp modOp, SymbolTableCollection &symTables) {
5459 SmallVector<FuncDefOp> funcs = walkCollect<FuncDefOp>(modOp);
5460 for (
unsigned promotionRounds = 1;; ++promotionRounds) {
5461 SmallVector<PromotedFunctionSignature> promotedSignatures;
5463 if (
auto promoted = promoteFunctionArgCasts(func)) {
5464 promotedSignatures.push_back(std::move(*promoted));
5467 if (promotedSignatures.empty()) {
5470 if (failed(updateCallsForPromotedFunctionArgCasts(modOp, symTables, promotedSignatures))) {
5473 if (promotionRounds % 64 == 0) {
5474 llvm::outs() <<
"function argument cast promotion has run " << promotionRounds
5475 <<
" rounds without reaching a fixpoint; continuing...\n";
5480void Step3Resolver::rehydrateVirtualPodPlaceholders(ModuleOp modOp) {
5481 ::rehydrateVirtualPodPlaceholders(modOp, virtualPods);
5484void Step3Resolver::addPreConversionPatterns(RewritePatternSet &patterns) {
5485 patterns.add<ResolveDeferredSplitPodArrayCastPrepass>(patterns.getContext(), *
this);
5488void Step3Resolver::addConversionPatterns(
5489 RewritePatternSet &patterns, SymbolTableCollection &symTables,
5490 const MemberReplacementMap &memberRepMap
5492 patterns.add<SplitInitFromNewPodOp>(patterns.getContext());
5493 patterns.add<SplitPodElementCreateArrayOp>(patterns.getContext(), *
this);
5494 patterns.add<SplitPodInFuncDefOp, SplitPodInReturnOp, SplitPodInCallOp>(
5495 patterns.getContext(), *
this
5497 patterns.add<SplitPodInMemberWriteOp, SplitPodInMemberReadOp>(
5498 patterns.getContext(), symTables, memberRepMap, *
this
5500 patterns.add<RejectRaggedNestedLeafArrayLengthOp>(patterns.getContext());
5501 patterns.add<ResolvePodReadBackedArrayReadOp>(patterns.getContext(), *
this);
5502 patterns.add<ResolvePodReadBackedArrayLengthOp>(patterns.getContext(), *
this);
5503 patterns.add<ResolveDeferredSplitPodArrayCastOp>(patterns.getContext(), *
this);
5504 patterns.add<ResolveVirtualPodWriteOp>(patterns.getContext(), *
this);
5505 patterns.add<ResolveVirtualPodReadOp>(patterns.getContext(), *
this);
5508void Step3Resolver::addLateResolutionPatterns(RewritePatternSet &patterns) {
5509 patterns.add<ResolvePodReadBackedArrayReadOp>(patterns.getContext(), *
this);
5510 patterns.add<ResolvePodReadBackedArrayLengthOp>(patterns.getContext(), *
this);
5511 patterns.add<ResolveDeferredSplitPodArrayCastOp>(patterns.getContext(), *
this);
5512 patterns.add<ResolveVirtualPodWriteOp>(patterns.getContext(), *
this);
5513 patterns.add<ResolveVirtualPodReadOp>(patterns.getContext(), *
this);
5516void Step3Resolver::addPostConversionPatterns(RewritePatternSet &patterns) {
5517 patterns.add<SplitVirtualPodInEmitEqualityPattern>(patterns.getContext(), *
this);
5520bool Step3Resolver::canResolveVirtualPodReadFromAnalysis(ReadPodOp op)
const {
5521 if (!podAccesses || !podAccesses->hasFacts(op) || podAccesses->hasEarlierWrite(op) ||
5522 podAccesses->hasForwardableWrite(op)) {
5525 return canResolveVirtualPodRead(op, virtualPods);
5528void Step3Resolver::configureLateVirtualPodLegality(ConversionTarget &target)
const {
5529 target.addDynamicallyLegalOp<WritePodOp>([
this](WritePodOp op) {
5530 return !canResolveVirtualPodWrite(op, virtualPods);
5532 target.addDynamicallyLegalOp<ArrayLengthOp>([](ArrayLengthOp op) {
5533 return RejectRaggedNestedLeafArrayLengthOp::legal(op) &&
5534 ResolvePodReadBackedArrayLengthOp::legal(op);
5536 target.addDynamicallyLegalOp<ReadArrayOp>([
this](ReadArrayOp op) {
5537 return !ResolvePodReadBackedArrayReadOp::canResolve(op, *
this);
5539 target.addDynamicallyLegalOp<UnrealizedConversionCastOp>([
this](UnrealizedConversionCastOp op) {
5540 return !ResolveDeferredSplitPodArrayCastOp::canResolve(op, *
this);
5542 target.addDynamicallyLegalOp<ReadPodOp>([
this](ReadPodOp op) {
5543 return !canResolveVirtualPodReadFromAnalysis(op);
5547bool Step3Resolver::hasResolvableLateVirtualPodOps(ModuleOp modOp) {
5548 refreshPodAccessAnalysis(modOp);
5549 return walkContains<Operation *>(*modOp, [
this](Operation *op) {
5550 return TypeSwitch<Operation *, bool>(op)
5551 .Case<WritePodOp>([
this](
auto writeOp) {
5552 return canResolveVirtualPodWrite(writeOp, virtualPods);
5554 .Case<ReadPodOp>([
this](
auto readOp) {
5555 return canResolveVirtualPodReadFromAnalysis(readOp);
5557 .Case<ReadArrayOp>([
this](
auto readOp) {
5558 return ResolvePodReadBackedArrayReadOp::canResolve(readOp, *
this);
5560 .Case<ArrayLengthOp>([](
auto lenOp) {
5561 return !ResolvePodReadBackedArrayLengthOp::legal(lenOp);
5563 .Case<UnrealizedConversionCastOp>([
this](
auto castOp) {
5564 return ResolveDeferredSplitPodArrayCastOp::canResolve(castOp, *
this);
5565 }).Default([](Operation *) {
return false; });
5569void Step3Resolver::materializeRemainingVirtualPods(ModuleOp modOp) {
5570 OpBuilder builder(modOp.getContext());
5571 modOp.walk([
this, &builder](NewPodOp newPod) {
5572 auto it = virtualPods.find(newPod.
getResult());
5573 if (it == virtualPods.end() || newPod.use_empty()) {
5576 builder.setInsertionPointAfter(findVirtualPodMaterializationAnchor(newPod, it->second));
5577 materializeVirtualPod(builder, newPod, it->second);
5584step3(ModuleOp modOp, SymbolTableCollection &symTables,
const MemberReplacementMap &memberRepMap) {
5585 MLIRContext *ctx = modOp.getContext();
5586 Step3Resolver resolver;
5587 resolver.rehydrateVirtualPodPlaceholders(modOp);
5589 RewritePatternSet preConversionPatterns(ctx);
5590 resolver.addPreConversionPatterns(preConversionPatterns);
5591 if (failed(applyPatternsGreedily(
5592 modOp->getRegion(0), std::move(preConversionPatterns),
5593 GreedyRewriteConfig().enableFolding(
false).enableConstantCSE(
false)
5598 RewritePatternSet patterns(ctx);
5599 resolver.addConversionPatterns(patterns, symTables, memberRepMap);
5601 resolver.refreshPodAccessAnalysis(modOp);
5603 ConversionTarget target(*ctx);
5604 baseTargetSetup(target);
5605 target.addDynamicallyLegalOp<NewPodOp>(SplitInitFromNewPodOp::legal);
5606 target.addDynamicallyLegalOp<CreateArrayOp>(SplitPodElementCreateArrayOp::legal);
5607 target.addDynamicallyLegalOp<FuncDefOp>(SplitPodInFuncDefOp::legal);
5608 target.addDynamicallyLegalOp<ReturnOp>(SplitPodInReturnOp::legal);
5609 target.addDynamicallyLegalOp<CallOp>(SplitPodInCallOp::legal);
5610 target.addDynamicallyLegalOp<MemberWriteOp>(SplitPodInMemberWriteOp::legal);
5611 target.addDynamicallyLegalOp<MemberReadOp>(SplitPodInMemberReadOp::legal);
5612 resolver.configureLateVirtualPodLegality(target);
5614 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 3: update/split other pod ops\n";);
5615 if (failed(applyFullConversion(modOp, target, std::move(patterns)))) {
5628 auto runLateResolutionRound = [&]() {
5629 RewritePatternSet lateResolutionPatterns(ctx);
5630 resolver.addLateResolutionPatterns(lateResolutionPatterns);
5632 ConversionTarget lateResolutionTarget(*ctx);
5633 baseTargetSetup(lateResolutionTarget);
5634 lateResolutionTarget.addLegalOp<ModuleOp>();
5635 resolver.configureLateVirtualPodLegality(lateResolutionTarget);
5637 return applyPartialConversion(modOp, lateResolutionTarget, std::move(lateResolutionPatterns));
5642 for (
unsigned lateResolutionRounds = 0;
5643 lateResolutionRounds < 64 && resolver.hasResolvableLateVirtualPodOps(modOp);
5644 ++lateResolutionRounds) {
5645 if (failed(runLateResolutionRound())) {
5649 if (resolver.hasResolvableLateVirtualPodOps(modOp)) {
5650 return modOp.emitError(
"late virtual POD resolution did not reach a fixpoint");
5653 RewritePatternSet postConversionPatterns(ctx);
5654 resolver.addPostConversionPatterns(postConversionPatterns);
5655 if (failed(applyPatternsGreedily(
5656 modOp->getRegion(0), std::move(postConversionPatterns),
5657 GreedyRewriteConfig().enableFolding(
false).enableConstantCSE(
false)
5661 if (failed(promoteFunctionArgCastsToSignature(modOp, symTables))) {
5665 resolver.materializeRemainingVirtualPods(modOp);
5667 bool erasedDeadPlaceholderOps =
false;
5669 SmallVector<Operation *> deadPlaceholderOps;
5670 modOp->walk<WalkOrder::PostOrder>([&deadPlaceholderOps](Operation *op) {
5671 if (
auto readOp = llvm::dyn_cast<ReadPodOp>(op)) {
5673 deadPlaceholderOps.push_back(op);
5678 if (
auto castOp = llvm::dyn_cast<UnrealizedConversionCastOp>(op)) {
5679 if (llvm::all_of(castOp.getResults(), [](Value result) { return result.use_empty(); })) {
5680 deadPlaceholderOps.push_back(op);
5684 for (Operation *op : deadPlaceholderOps) {
5687 erasedDeadPlaceholderOps = !deadPlaceholderOps.empty();
5688 }
while (erasedDeadPlaceholderOps);
5690 SmallVector<Operation *> deadOps;
5691 modOp->walk<WalkOrder::PostOrder>([&](Operation *op) {
5692 if (op != modOp.getOperation() && isOpTriviallyDead(op)) {
5693 deadOps.push_back(op);
5696 for (Operation *op : deadOps) {
5706static bool isValueDefinedInside(Operation *ancestor, Value value) {
5707 if (Operation *defOp = value.getDefiningOp()) {
5708 return ancestor->isAncestor(defOp);
5711 auto blockArg = llvm::dyn_cast<BlockArgument>(value);
5712 Operation *parentOp = blockArg.getOwner()->getParentOp();
5713 return parentOp && ancestor->isAncestor(parentOp);
5717static FailureOr<Value>
5718materializePodAccessSnapshot(OpBuilder &builder, Location loc, Value
source) {
5723class FoldReadAfterWriteInBlockPattern final :
public OpRewritePattern<ReadPodOp> {
5725 using OpRewritePattern<ReadPodOp>::OpRewritePattern;
5727 LogicalResult matchAndRewrite(ReadPodOp readOp, PatternRewriter &rewriter)
const override {
5728 if (WritePodOp writeOp = findNearestForwardableWriteInBlock(readOp)) {
5729 Value replacement = writeOp.
getValue();
5733 while (
auto sourceRead = replacement.getDefiningOp<ReadPodOp>()) {
5734 WritePodOp sourceWrite = findNearestForwardableWriteInBlock(sourceRead);
5738 replacement = sourceWrite.
getValue();
5744 rewriter.replaceOp(readOp, replacement);
5752class ReplaceIfReadPattern final :
public OpRewritePattern<ReadPodOp> {
5754 using OpRewritePattern<ReadPodOp>::OpRewritePattern;
5756 LogicalResult matchAndRewrite(ReadPodOp readOp, PatternRewriter &rewriter)
const override {
5760 auto ifOp = readOp->getParentOfType<scf::IfOp>();
5761 if (!ifOp || readOp->getBlock()->getParentOp() != ifOp.getOperation()) {
5764 if (isValueDefinedInside(ifOp, readOp.
getPodRef()) || hasEarlierWriteInBlock(readOp)) {
5771 Operation *anchor = ifOp.getOperation();
5773 if (WritePodOp writeOp = findNearestForwardableWriteBefore(
5781 rewriter.replaceOp(readOp, writeOp.
getValue());
5788 auto parentIf = dyn_cast_or_null<scf::IfOp>(anchor->getBlock()->getParentOp());
5789 if (!parentIf || isValueDefinedInside(parentIf, readOp.
getPodRef())) {
5792 anchor = parentIf.getOperation();
5795 rewriter.setInsertionPoint(anchor);
5799 rewriter.setInsertionPoint(readOp);
5800 FailureOr<Value> copied = materializePodAccessSnapshot(rewriter, readOp.getLoc(),
source);
5801 if (failed(copied)) {
5804 rewriter.replaceOp(readOp, *copied);
5822class FoldIfCarriedPodReadAfterWritePattern final :
public OpRewritePattern<ReadPodOp> {
5824 using OpRewritePattern<ReadPodOp>::OpRewritePattern;
5826 LogicalResult matchAndRewrite(ReadPodOp readOp, PatternRewriter &rewriter)
const override {
5827 auto podRes = dyn_cast<OpResult>(readOp.
getPodRef());
5832 auto ifOp = dyn_cast<scf::IfOp>(podRes.getOwner());
5837 auto writeOp = dyn_cast_or_null<WritePodOp>(readOp->getPrevNode());
5842 auto valueRes = dyn_cast<OpResult>(writeOp.
getValue());
5843 if (!valueRes || valueRes.getOwner() != ifOp.getOperation()) {
5848 unsigned podResultIndex = podRes.getResultNumber();
5850 auto thenYield = dyn_cast<scf::YieldOp>(ifOp.thenBlock()->getTerminator());
5851 if (!thenYield || thenYield.getOperand(podResultIndex) != carriedPod) {
5855 Region &elseRegion = ifOp.getElseRegion();
5856 if (Block *elseBlock = elseRegion.empty() ?
nullptr : &elseRegion.front()) {
5857 auto elseYield = dyn_cast<scf::YieldOp>(elseBlock->getTerminator());
5858 if (!elseYield || elseYield.getOperand(podResultIndex) != carriedPod) {
5866 rewriter.setInsertionPoint(readOp);
5867 FailureOr<Value> copied = materializePodAccessSnapshot(rewriter, readOp.getLoc(), valueRes);
5868 if (failed(copied)) {
5871 rewriter.replaceOp(readOp, *copied);
5882 StringAttr recordName;
5884 WritePodOp thenWrite;
5885 WritePodOp elseWrite;
5886 SmallVector<Value> incomingValues;
5887 SmallVector<Value> thenValues;
5888 SmallVector<Value> elseValues;
5893lookupSlot(SmallVectorImpl<IfWriteSlot> &slots, Value podRef, StringAttr recordName) {
5894 for (IfWriteSlot &slot : slots) {
5895 if (slot.podRef == podRef && slot.recordName == recordName) {
5903static IfWriteSlot &getOrCreateSlot(
5904 SmallVectorImpl<IfWriteSlot> &slots, Value podRef, StringAttr recordName, Type type
5906 if (IfWriteSlot *slot = lookupSlot(slots, podRef, recordName)) {
5909 slots.push_back(IfWriteSlot {podRef, recordName, type,
nullptr,
nullptr, {}, {}, {}});
5910 return slots.back();
5914static Block *getElseBlockOrNull(scf::IfOp ifOp) {
5915 return ifOp.getElseRegion().empty() ? nullptr : &ifOp.getElseRegion().front();
5920collectDirectWrites(Block *block,
bool isThenBlock, SmallVectorImpl<IfWriteSlot> &slots) {
5925 for (Operation &op : *block) {
5926 if (op.hasTrait<OpTrait::IsTerminator>()) {
5930 auto writeOp = dyn_cast<WritePodOp>(&op);
5935 IfWriteSlot &slot = getOrCreateSlot(
5939 slot.thenWrite = writeOp;
5941 slot.elseWrite = writeOp;
5950static bool branchSlotCanBeLifted(Block *block, Value podRef, StringAttr recordName) {
5955 bool seenDirectWrite =
false;
5956 for (Operation &op : *block) {
5957 if (op.hasTrait<OpTrait::IsTerminator>()) {
5961 if (
auto writeOp = dyn_cast<WritePodOp>(&op)) {
5962 if (isSamePodRecord(writeOp, podRef, recordName)) {
5963 seenDirectWrite =
true;
5968 if (hasNestedWriteToRecord(op, podRef, recordName)) {
5971 if (seenDirectWrite && (hasReadFromRecord(op, podRef, recordName) || hasValueUse(op, podRef))) {
5979static bool isLiftedWrite(Operation &op, ArrayRef<IfWriteSlot> slots) {
5980 auto writeOp = dyn_cast<WritePodOp>(&op);
5981 return writeOp && llvm::any_of(slots, [&writeOp](
const IfWriteSlot &slot) {
5982 return isSamePodRecord(writeOp, slot.podRef, slot.recordName);
5987static scf::YieldOp getYieldOp(Block &block) {
5988 auto yieldOp = dyn_cast<scf::YieldOp>(block.getTerminator());
5989 assert(yieldOp &&
"expected scf.if branch to terminate with scf.yield");
5994static void dropTerminatorIfPresent(Block &block) {
5995 if (!block.empty() && block.back().hasTrait<OpTrait::IsTerminator>()) {
5996 block.back().erase();
6001static LogicalResult appendIfWriteSlotValues(
6002 OpBuilder &builder, Location loc, Type type, Value value, SmallVectorImpl<Value> &values
6004 auto podType = dyn_cast<PodType>(type);
6006 FailureOr<Value> copied = materializePodAccessSnapshot(builder, loc, value);
6007 if (failed(copied)) {
6010 values.push_back(*copied);
6014 bool failedToCopy =
false;
6015 SmallVector<StringAttr> recordChain;
6017 podType, recordChain,
6018 [&builder, loc, value, &values, &failedToCopy](
const RecordChain &
id, Type) {
6019 Value leaf = genReadAlongPath(builder, loc, value,
id);
6020 FailureOr<Value> copied = materializePodAccessSnapshot(builder, loc, leaf);
6021 if (failed(copied)) {
6022 failedToCopy =
true;
6025 values.push_back(*copied);
6028 return failure(failedToCopy);
6032static void appendIfWriteSlotTypes(Type type, SmallVectorImpl<Type> &types) {
6033 auto podType = dyn_cast<PodType>(type);
6035 types.push_back(type);
6039 SmallVector<StringAttr> recordChain;
6040 forEachPodLeaf(podType, recordChain, [&types](
const RecordChain &, Type leafType) {
6041 types.push_back(leafType);
6047moveBranchWithoutLiftedWrites(Block *srcBlock, Block &destBlock, ArrayRef<IfWriteSlot> slots) {
6049 for (
auto it = srcBlock->begin(), end = srcBlock->end(); it != end;) {
6050 Operation &op = *it++;
6051 if (op.hasTrait<OpTrait::IsTerminator>() || isLiftedWrite(op, slots)) {
6054 op.moveBefore(&destBlock, destBlock.end());
6061static void appendYield(
6062 OpBuilder &bldr, Location loc, Block &block, ValueRange priorYieldValues,
6063 ArrayRef<IfWriteSlot> slots,
bool isThenBlock, scf::YieldOp originalYield =
nullptr
6065 SmallVector<Value> yieldValues = llvm::to_vector(priorYieldValues);
6066 bldr.setInsertionPointToEnd(&block);
6067 for (
const IfWriteSlot &slot : slots) {
6068 ArrayRef<Value> branchValues = isThenBlock ? slot.thenValues : slot.elseValues;
6069 llvm::append_range(yieldValues, branchValues.empty() ? slot.incomingValues : branchValues);
6072 auto newYield = scf::YieldOp::create(bldr, loc, yieldValues);
6073 if (originalYield) {
6085 StringAttr recordName;
6089 bool matches(Value findPodRef, StringAttr findRecordName)
const {
6090 return this->podRef == findPodRef && this->recordName == findRecordName;
6096lookupLoopSlot(SmallVectorImpl<LoopPodSlot> &slots, Value podRef, StringAttr recordName) {
6097 auto *it = llvm::find_if(slots, [&podRef, &recordName](
const LoopPodSlot &slot) {
6098 return slot.matches(podRef, recordName);
6100 return it == slots.end() ? nullptr : &*it;
6104static bool hasLoopSlot(ArrayRef<LoopPodSlot> slots, Value podRef, StringAttr recordName) {
6105 const auto *it = llvm::find_if(slots, [&podRef, &recordName](
const LoopPodSlot &slot) {
6106 return slot.matches(podRef, recordName);
6108 return it != slots.end();
6112static LoopPodSlot &getOrCreateLoopSlot(
6113 SmallVectorImpl<LoopPodSlot> &slots, Value podRef, StringAttr recordName, Type type
6115 if (LoopPodSlot *slot = lookupLoopSlot(slots, podRef, recordName)) {
6118 slots.push_back(LoopPodSlot {podRef, recordName, type});
6119 return slots.back();
6123static std::optional<size_t>
6124findLoopSlotIndex(ArrayRef<LoopPodSlot> slots, Value podRef, StringAttr recordName) {
6125 for (
auto [idx, slot] : llvm::enumerate(slots)) {
6126 if (slot.podRef == podRef && slot.recordName == recordName) {
6130 return std::nullopt;
6136collectDirectLoopPodSlots(Block &block, Operation *ancestor, SmallVectorImpl<LoopPodSlot> &slots) {
6137 for (Operation &op : block) {
6138 if (
auto readOp = dyn_cast<ReadPodOp>(&op)) {
6139 if (!isValueDefinedInside(ancestor, readOp.
getPodRef())) {
6140 getOrCreateLoopSlot(
6147 if (
auto writeOp = dyn_cast<WritePodOp>(&op)) {
6148 if (!isValueDefinedInside(ancestor, writeOp.
getPodRef())) {
6149 getOrCreateLoopSlot(
6158static bool opUsesTrackedPodRefDirectly(Operation &op, ArrayRef<LoopPodSlot> slots) {
6159 return llvm::any_of(op.getOperands(), [&slots](Value operand) {
6160 return llvm::any_of(slots, [&operand](const LoopPodSlot &slot) {
6161 return slot.podRef == operand;
6167static bool hasNestedTrackedPodAccess(Operation &op, ArrayRef<LoopPodSlot> slots) {
6169 .walk([&op, &slots](Operation *nestedOp) {
6170 if (nestedOp == &op) {
6171 return WalkResult::advance();
6174 if (
auto readOp = dyn_cast<ReadPodOp>(nestedOp)) {
6176 return WalkResult::interrupt();
6178 return WalkResult::advance();
6181 if (
auto writeOp = dyn_cast<WritePodOp>(nestedOp)) {
6183 return WalkResult::interrupt();
6186 return WalkResult::advance();
6187 }).wasInterrupted();
6192static bool hasUnliftableLoopPodUses(Block &block, ArrayRef<LoopPodSlot> slots) {
6193 for (Operation &op : block) {
6194 if (isa<ReadPodOp, WritePodOp>(op)) {
6197 if (opUsesTrackedPodRefDirectly(op, slots) || hasNestedTrackedPodAccess(op, slots)) {
6205template <
typename RewriteTerminatorFn>
6206static void cloneLoopBodyWithLiftedPodSlots(
6207 Block &
source, PatternRewriter &rewriter, IRMapping &mapping, ArrayRef<LoopPodSlot> slots,
6208 SmallVectorImpl<Value> &slotValues, RewriteTerminatorFn &&rewriteTerminator
6210 for (Operation &op :
source) {
6211 if (rewriteTerminator(op)) {
6215 if (
auto readOp = dyn_cast<ReadPodOp>(&op)) {
6216 if (std::optional<size_t> slotIdx =
6218 FailureOr<Value> copied =
6219 materializePodAccessSnapshot(rewriter, readOp.getLoc(), slotValues[*slotIdx]);
6220 assert(succeeded(copied) &&
"copy support was checked before loop lifting");
6221 mapping.map(readOp.
getResult(), *copied);
6226 if (
auto writeOp = dyn_cast<WritePodOp>(&op)) {
6227 if (std::optional<size_t> slotIdx =
6229 FailureOr<Value> copied = materializePodAccessSnapshot(
6230 rewriter, writeOp.getLoc(), mapping.lookupOrDefault(writeOp.
getValue())
6232 assert(succeeded(copied) &&
"copy support was checked before loop lifting");
6233 slotValues[*slotIdx] = *copied;
6238 rewriter.clone(op, mapping);
6243static void appendIncomingLoopSlotValues(
6244 PatternRewriter &rewriter, Location loc, ArrayRef<LoopPodSlot> slots,
6245 SmallVectorImpl<Value> &values, SmallVectorImpl<Type> *resultTypes =
nullptr
6247 for (
const LoopPodSlot &slot : slots) {
6248 values.push_back(genRead(rewriter, loc, slot.podRef, slot.recordName).getResult());
6250 resultTypes->push_back(slot.type);
6256template <
typename ValueRangeLike>
6257static SmallVector<Value>
6258collectTrailingLoopSlotValues(ValueRangeLike values,
size_t base,
size_t slotCount) {
6259 SmallVector<Value> slotValues;
6260 slotValues.reserve(slotCount);
6261 for (
size_t idx = 0; idx < slotCount; ++idx) {
6268static SmallVector<Value>
6269remapValuesAndAppendLoopSlots(ValueRange values, IRMapping &mapping, ValueRange slotValues) {
6270 SmallVector<Value> remappedValues = llvm::map_to_vector(values, [&mapping](Value value) {
6271 return mapping.lookupOrDefault(value);
6273 llvm::append_range(remappedValues, slotValues);
6274 return remappedValues;
6278template <
typename GetResultFn>
6279static void writeBackLoopSlotResults(
6280 PatternRewriter &rewriter, Location loc, ArrayRef<LoopPodSlot> slots,
6281 unsigned originalResultCount, GetResultFn &&getResult
6283 for (
auto [idx, slot] : llvm::enumerate(slots)) {
6285 rewriter, loc, slot.podRef, slot.recordName,
6294class LiftPodWritesFromIfBlocksPattern final :
public OpRewritePattern<scf::IfOp> {
6296 using OpRewritePattern<scf::IfOp>::OpRewritePattern;
6298 LogicalResult matchAndRewrite(scf::IfOp ifOp, PatternRewriter &rewriter)
const override {
6299 SmallVector<IfWriteSlot> slots;
6300 Block &thenBlock = *ifOp.thenBlock();
6301 Block *elseBlock = getElseBlockOrNull(ifOp);
6302 collectDirectWrites(&thenBlock,
true, slots);
6303 collectDirectWrites(elseBlock,
false, slots);
6304 if (slots.empty()) {
6308 llvm::erase_if(slots, [ifOp, &thenBlock, elseBlock](
const IfWriteSlot &slot) {
6309 return isValueDefinedInside(ifOp, slot.podRef) ||
6311 !branchSlotCanBeLifted(&thenBlock, slot.podRef, slot.recordName) ||
6312 !branchSlotCanBeLifted(elseBlock, slot.podRef, slot.recordName);
6314 if (slots.empty()) {
6318 for (IfWriteSlot &slot : slots) {
6319 if (slot.thenWrite) {
6320 rewriter.setInsertionPoint(slot.thenWrite);
6321 LogicalResult result = appendIfWriteSlotValues(
6322 rewriter, slot.thenWrite.getLoc(), slot.type, slot.thenWrite.
getValue(), slot.thenValues
6324 assert(succeeded(result) &&
"copy support was checked before if lifting");
6327 if (slot.elseWrite) {
6328 rewriter.setInsertionPoint(slot.elseWrite);
6329 LogicalResult result = appendIfWriteSlotValues(
6330 rewriter, slot.elseWrite.getLoc(), slot.type, slot.elseWrite.
getValue(), slot.elseValues
6332 assert(succeeded(result) &&
"copy support was checked before if lifting");
6335 if (slot.thenWrite && slot.elseWrite) {
6338 rewriter.setInsertionPoint(ifOp);
6339 Value incomingValue =
6340 genRead(rewriter, ifOp.getLoc(), slot.podRef, slot.recordName).getResult();
6341 LogicalResult result = appendIfWriteSlotValues(
6342 rewriter, ifOp.getLoc(), slot.type, incomingValue, slot.incomingValues
6344 assert(succeeded(result) &&
"copy support was checked before if lifting");
6348 SmallVector<Type> resultTypes = llvm::to_vector(ifOp.getResultTypes());
6349 for (
const IfWriteSlot &slot : slots) {
6350 appendIfWriteSlotTypes(slot.type, resultTypes);
6353 scf::YieldOp thenYieldOp = getYieldOp(thenBlock);
6354 SmallVector<Value> originalThenYields;
6355 if (!ifOp.getResults().empty()) {
6356 originalThenYields.append(thenYieldOp.getOperands().begin(), thenYieldOp.getOperands().end());
6359 scf::YieldOp elseYieldOp = elseBlock ? getYieldOp(*elseBlock) : nullptr;
6360 SmallVector<Value> originalElseYields;
6361 if (elseBlock && !ifOp.getResults().empty()) {
6362 originalElseYields.append(elseYieldOp.getOperands().begin(), elseYieldOp.getOperands().end());
6365 rewriter.setInsertionPoint(ifOp);
6366 auto newIf = scf::IfOp::create(rewriter, ifOp.getLoc(), resultTypes, ifOp.getCondition(),
true);
6367 Block &newThenBlock = *newIf.thenBlock();
6368 Block &newElseBlock = *newIf.elseBlock();
6369 dropTerminatorIfPresent(newThenBlock);
6370 dropTerminatorIfPresent(newElseBlock);
6372 moveBranchWithoutLiftedWrites(&thenBlock, newThenBlock, slots);
6373 moveBranchWithoutLiftedWrites(elseBlock, newElseBlock, slots);
6375 rewriter, ifOp.getLoc(), newThenBlock, originalThenYields, slots,
true, thenYieldOp
6378 rewriter, ifOp.getLoc(), newElseBlock, originalElseYields, slots,
false, elseYieldOp
6381 rewriter.setInsertionPointAfter(newIf);
6382 unsigned resultIndex = ifOp.getNumResults();
6383 for (
const IfWriteSlot &slot : slots) {
6385 if (
auto podType = dyn_cast<PodType>(slot.type)) {
6386 VirtualPodLeafMap leafValues;
6387 SmallVector<StringAttr> recordChain;
6389 podType, recordChain, [&leafValues, &newIf, &resultIndex](
const RecordChain &
id, Type) {
6390 leafValues[id] = newIf.getResult(resultIndex++);
6393 SmallVector<StringAttr> rootRecordChain;
6394 resultValue = rebuildFlattenedPodRecord(
6395 rewriter, ifOp.getLoc(), podType, rootRecordChain, leafValues
6398 resultValue = newIf.getResult(resultIndex++);
6400 genWrite(rewriter, ifOp.getLoc(), slot.podRef, slot.recordName, resultValue);
6403 rewriter.replaceOp(ifOp, newIf.getResults().take_front(ifOp.getNumResults()));
6410class LiftPodAccessesFromForLoopPattern final :
public OpRewritePattern<scf::ForOp> {
6412 using OpRewritePattern<scf::ForOp>::OpRewritePattern;
6414 LogicalResult matchAndRewrite(scf::ForOp forOp, PatternRewriter &rewriter)
const override {
6415 Block &body = *forOp.getBody();
6416 SmallVector<LoopPodSlot> slots;
6417 collectDirectLoopPodSlots(body, forOp.getOperation(), slots);
6418 if (slots.empty() ||
6421 [](
const LoopPodSlot &slot) { return llzk::requiresUnsupportedValueCopy(slot.type); }
6423 hasUnliftableLoopPodUses(body, slots)) {
6427 Location loc = forOp.getLoc();
6429 SmallVector<Value> newInitArgs = llvm::to_vector(forOp.getInitArgs());
6430 rewriter.setInsertionPoint(forOp);
6431 appendIncomingLoopSlotValues(rewriter, loc, slots, newInitArgs);
6433 auto newFor = scf::ForOp::create(
6434 rewriter, loc, forOp.getLowerBound(), forOp.getUpperBound(), forOp.getStep(), newInitArgs
6436 newFor->setAttrs(forOp->getAttrs());
6438 Block &newBody = *newFor.getBody();
6439 dropTerminatorIfPresent(newBody);
6442 mapping.map(forOp.getInductionVar(), newFor.getInductionVar());
6443 for (
auto [idx, oldArg] : llvm::enumerate(forOp.getRegionIterArgs())) {
6444 mapping.map(oldArg, newFor.getRegionIterArg(idx));
6447 SmallVector<Value> slotValues = collectTrailingLoopSlotValues(
6448 newFor.getRegionIterArgs(), forOp.getNumRegionIterArgs(), slots.size()
6451 rewriter.setInsertionPointToEnd(&newBody);
6452 cloneLoopBodyWithLiftedPodSlots(body, rewriter, mapping, slots, slotValues, [&](Operation &op) {
6453 if (
auto yieldOp = dyn_cast<scf::YieldOp>(&op)) {
6454 SmallVector<Value> yieldValues =
6455 remapValuesAndAppendLoopSlots(yieldOp.getOperands(), mapping, slotValues);
6457 yieldOp, scf::YieldOp::create(rewriter, yieldOp.getLoc(), yieldValues)
6464 rewriter.setInsertionPointAfter(newFor);
6465 writeBackLoopSlotResults(
6466 rewriter, loc, slots, forOp.getNumResults(),
6467 [&newFor](
unsigned resultIdx) { return newFor.getResult(resultIdx); }
6470 rewriter.replaceOp(forOp, newFor.getResults().take_front(forOp.getNumResults()));
6477class LiftPodAccessesFromWhileLoopPattern final :
public OpRewritePattern<scf::WhileOp> {
6479 using OpRewritePattern<scf::WhileOp>::OpRewritePattern;
6481 LogicalResult matchAndRewrite(scf::WhileOp whileOp, PatternRewriter &rewriter)
const override {
6482 Block &beforeBody = *whileOp.getBeforeBody();
6483 Block &afterBody = *whileOp.getAfterBody();
6485 SmallVector<LoopPodSlot> slots;
6486 collectDirectLoopPodSlots(beforeBody, whileOp.getOperation(), slots);
6487 collectDirectLoopPodSlots(afterBody, whileOp.getOperation(), slots);
6488 if (slots.empty() ||
6491 [](
const LoopPodSlot &slot) { return llzk::requiresUnsupportedValueCopy(slot.type); }
6493 hasUnliftableLoopPodUses(beforeBody, slots) || hasUnliftableLoopPodUses(afterBody, slots)) {
6497 Location loc = whileOp.getLoc();
6499 SmallVector<Value> newInits = llvm::to_vector(whileOp.getInits());
6500 SmallVector<Type> newResultTypes = llvm::to_vector(whileOp.getResultTypes());
6501 rewriter.setInsertionPoint(whileOp);
6502 appendIncomingLoopSlotValues(rewriter, loc, slots, newInits, &newResultTypes);
6504 auto newWhile = scf::WhileOp::create(rewriter, loc, newResultTypes, newInits,
nullptr,
nullptr);
6505 newWhile->setAttrs(whileOp->getAttrs());
6507 Block &newBeforeBody = *newWhile.getBeforeBody();
6508 Block &newAfterBody = *newWhile.getAfterBody();
6509 dropTerminatorIfPresent(newBeforeBody);
6510 dropTerminatorIfPresent(newAfterBody);
6512 IRMapping beforeMapping;
6513 for (
auto [oldArg, newArg] : llvm::zip_equal(
6514 whileOp.getBeforeArguments(),
6515 newWhile.getBeforeArguments().take_front(whileOp.getBeforeArguments().size())
6517 beforeMapping.map(oldArg, newArg);
6520 SmallVector<Value> beforeSlotValues = collectTrailingLoopSlotValues(
6521 newWhile.getBeforeArguments(), whileOp.getBeforeArguments().size(), slots.size()
6524 rewriter.setInsertionPointToEnd(&newBeforeBody);
6525 cloneLoopBodyWithLiftedPodSlots(
6526 beforeBody, rewriter, beforeMapping, slots, beforeSlotValues, [&](Operation &op) {
6527 if (
auto conditionOp = dyn_cast<scf::ConditionOp>(&op)) {
6528 SmallVector<Value> conditionArgs =
6529 remapValuesAndAppendLoopSlots(conditionOp.getArgs(), beforeMapping, beforeSlotValues);
6532 scf::ConditionOp::create(
6533 rewriter, conditionOp.getLoc(),
6534 beforeMapping.lookupOrDefault(conditionOp.getCondition()), conditionArgs
6543 IRMapping afterMapping;
6544 for (
auto [oldArg, newArg] : llvm::zip_equal(
6545 whileOp.getAfterArguments(),
6546 newWhile.getAfterArguments().take_front(whileOp.getAfterArguments().size())
6548 afterMapping.map(oldArg, newArg);
6551 SmallVector<Value> afterSlotValues = collectTrailingLoopSlotValues(
6552 newWhile.getAfterArguments(), whileOp.getAfterArguments().size(), slots.size()
6555 rewriter.setInsertionPointToEnd(&newAfterBody);
6556 cloneLoopBodyWithLiftedPodSlots(
6557 afterBody, rewriter, afterMapping, slots, afterSlotValues, [&](Operation &op) {
6558 if (
auto yieldOp = dyn_cast<scf::YieldOp>(&op)) {
6559 SmallVector<Value> yieldValues =
6560 remapValuesAndAppendLoopSlots(yieldOp.getOperands(), afterMapping, afterSlotValues);
6562 yieldOp, scf::YieldOp::create(rewriter, yieldOp.getLoc(), yieldValues)
6570 rewriter.setInsertionPointAfter(newWhile);
6571 writeBackLoopSlotResults(
6572 rewriter, loc, slots, whileOp.getNumResults(),
6573 [&newWhile](
unsigned resultIdx) { return newWhile.getResult(resultIdx); }
6576 rewriter.replaceOp(whileOp, newWhile.getResults().take_front(whileOp.getNumResults()));
6582struct WhileCarriedPod {
6583 unsigned originalIndex;
6585 SmallVector<RecordChain> leafPaths;
6586 SmallVector<Type> leafTypes;
6590static void collectWhileCarriedPodLeaves(WhileCarriedPod &pod) {
6591 SmallVector<StringAttr> recordChain;
6592 forEachPodLeaf(pod.type, recordChain, [&pod](
const RecordChain &path, Type type) {
6593 pod.leafPaths.push_back(path);
6594 pod.leafTypes.push_back(type);
6598static std::optional<unsigned>
6599findWhileCarriedLeafIndex(
const WhileCarriedPod &pod,
const RecordChain &path) {
6600 for (
auto [idx, leafPath] : llvm::enumerate(pod.leafPaths)) {
6601 if (leafPath == path) {
6605 return std::nullopt;
6611static BlockArgument findWhileCarriedPodRoot(Value podRef, SmallVectorImpl<StringAttr> &path) {
6612 while (
auto readOp = podRef.getDefiningOp<ReadPodOp>()) {
6616 std::reverse(path.begin(), path.end());
6617 return dyn_cast<BlockArgument>(podRef);
6623hasUnsupportedWhileCarriedPodUse(BlockArgument arg,
const WhileCarriedPod &pod, StringRef &reason) {
6624 auto reject = [&reason](StringRef message) {
6628 SmallVector<Value> podValues {arg};
6629 for (
size_t valueIdx = 0; valueIdx < podValues.size(); ++valueIdx) {
6630 Value podValue = podValues[valueIdx];
6631 for (Operation *user : podValue.getUsers()) {
6632 if (
auto readOp = dyn_cast<ReadPodOp>(user)) {
6633 if (readOp.
getPodRef() != podValue || readOp->getBlock() != arg.getOwner()) {
6634 return reject(
"POD reads in nested regions are unsupported");
6636 if (isa<PodType>(readOp.getType())) {
6637 podValues.push_back(readOp.
getResult());
6640 SmallVector<StringAttr> path;
6641 BlockArgument root = findWhileCarriedPodRoot(readOp.
getPodRef(), path);
6643 if (root == arg && findWhileCarriedLeafIndex(pod, RecordChain(path))) {
6646 return reject(
"POD read does not resolve to a carried scalar leaf");
6648 if (
auto writeOp = dyn_cast<WritePodOp>(user)) {
6652 return reject(
"writes to nested POD copies are unsupported");
6654 if (writeOp->getBlock() != arg.getOwner()) {
6655 return reject(
"POD writes in nested regions are unsupported");
6657 if (isa<PodType>(writeOp.
getValue().getType())) {
6658 return reject(
"aggregate POD write-back is unsupported");
6660 SmallVector<StringAttr> path;
6661 BlockArgument root = findWhileCarriedPodRoot(writeOp.
getPodRef(), path);
6663 if (root == arg && findWhileCarriedLeafIndex(pod, RecordChain(path))) {
6666 return reject(
"POD write does not resolve to a carried scalar leaf");
6670 if (podValue == arg && user->getBlock() == arg.getOwner()) {
6671 if (
auto conditionOp = dyn_cast<scf::ConditionOp>(user)) {
6672 if (arg.getArgNumber() < conditionOp.getArgs().size() &&
6673 conditionOp.getArgs()[arg.getArgNumber()] == arg) {
6677 if (
auto yieldOp = dyn_cast<scf::YieldOp>(user)) {
6678 if (arg.getArgNumber() < yieldOp.getOperands().size() &&
6679 yieldOp.getOperand(arg.getArgNumber()) == arg) {
6684 return reject(
"carried POD has an unsupported use or is forwarded in a different position");
6695hasSupportedWhilePodInitialization(scf::WhileOp whileOp, Value init, StringRef &reason) {
6696 auto reject = [&reason](StringRef message) {
6700 SmallVector<std::pair<Value, Operation *>> pending {{init,
nullptr}};
6701 llvm::DenseSet<Value> allocations;
6702 for (
size_t idx = 0; idx < pending.size(); ++idx) {
6703 auto [value, ownerWrite] = pending[idx];
6704 auto allocation = value.getDefiningOp<NewPodOp>();
6705 if (!allocation || !allocation.getInitialValues().empty() ||
6706 allocation->getBlock() != whileOp->getBlock() || !allocation->isBeforeInBlock(whileOp) ||
6707 !allocations.insert(value).second) {
6709 "initializer must use distinct fresh local pod.new operations without initial values"
6713 llvm::DenseSet<StringAttr> initializedChildren;
6714 for (Operation *user : value.getUsers()) {
6715 if (user == ownerWrite || (value == init && user == whileOp.getOperation())) {
6718 if (user->getBlock() != whileOp->getBlock() || !user->isBeforeInBlock(whileOp)) {
6719 return reject(
"initializer or nested source has uses outside pre-loop initialization");
6721 if (
auto read = dyn_cast<ReadPodOp>(user)) {
6723 if (read.
getPodRef() != value || isa<PodType>(read.getType())) {
6724 return reject(
"POD-valued reads of an initialization source are unsupported");
6728 if (
auto write = dyn_cast<WritePodOp>(user)) {
6731 if (write.getPodRef() != value) {
6732 return reject(
"initialization source is stored into another POD");
6734 if (ownerWrite && !user->isBeforeInBlock(ownerWrite)) {
6735 return reject(
"initialization source is modified after being copied into its parent");
6737 if (isa<PodType>(write.getValue().getType())) {
6738 if (!initializedChildren.insert(write.getRecordNameAttr()).second) {
6739 return reject(
"nested POD record is initialized more than once");
6741 pending.emplace_back(write.getValue(), user);
6746 return reject(
"initialization source has an unsupported user");
6748 auto podType = mlir::cast<PodType>(value.getType());
6749 for (RecordAttr record : podType.getRecords()) {
6750 if (isa<PodType>(record.getType()) && !initializedChildren.contains(record.getName())) {
6751 return reject(
"nested POD record is not initialized");
6753 if (
auto arrayTy = dyn_cast<ArrayType>(record.getType());
6754 arrayTy && isa<PodType>(arrayTy.getElementType())) {
6755 return reject(
"arrays of PODs in initialization are unsupported");
6764template <
typename RewriteTerminatorFn>
6765static void cloneWhileBodyWithCarriedPods(
6766 Block &
source, PatternRewriter &rewriter, IRMapping &mapping,
6767 ArrayRef<WhileCarriedPod> carriedPods, SmallVector<SmallVector<Value>> &podValues,
6768 RewriteTerminatorFn &&rewriteTerminator
6770 for (Operation &op :
source) {
6771 if (rewriteTerminator(op)) {
6775 auto findPod = [&](Value podRef, SmallVectorImpl<StringAttr> &path) -> std::optional<unsigned> {
6776 BlockArgument blockArg = findWhileCarriedPodRoot(podRef, path);
6777 if (!blockArg || blockArg.getOwner() != &
source) {
6778 return std::nullopt;
6780 for (
auto [idx, pod] : llvm::enumerate(carriedPods)) {
6781 if (pod.originalIndex == blockArg.getArgNumber()) {
6785 return std::nullopt;
6788 if (
auto readOp = dyn_cast<ReadPodOp>(&op)) {
6789 SmallVector<StringAttr> path;
6790 if (std::optional<unsigned> podIdx = findPod(readOp.
getPodRef(), path)) {
6792 if (!isa<PodType>(readOp.getType())) {
6793 std::optional<unsigned> leafIdx =
6794 findWhileCarriedLeafIndex(carriedPods[*podIdx], RecordChain(path));
6795 assert(leafIdx &&
"unsupported carried-POD read should have been rejected");
6796 mapping.map(readOp.
getResult(), podValues[*podIdx][*leafIdx]);
6802 if (
auto writeOp = dyn_cast<WritePodOp>(&op)) {
6803 SmallVector<StringAttr> path;
6804 if (std::optional<unsigned> podIdx = findPod(writeOp.
getPodRef(), path)) {
6806 std::optional<unsigned> leafIdx =
6807 findWhileCarriedLeafIndex(carriedPods[*podIdx], RecordChain(path));
6808 assert(leafIdx &&
"unsupported carried-POD write should have been rejected");
6809 podValues[*podIdx][*leafIdx] = mapping.lookupOrDefault(writeOp.
getValue());
6814 rewriter.clone(op, mapping);
6820static LogicalResult checkWhileCarriedPods(
6821 scf::WhileOp whileOp, SmallVectorImpl<WhileCarriedPod> &carriedPods, StringRef &reason
6823 auto reject = [&reason](StringRef message) {
6827 for (
auto [idx, init] : llvm::enumerate(whileOp.getInits())) {
6828 auto podTy = dyn_cast<PodType>(init.getType());
6833 collectWhileCarriedPodLeaves(pod);
6837 if (llvm::any_of(pod.leafTypes, [](Type type) { return isa<PodType, ArrayType>(type); })) {
6838 return reject(
"mutable aggregate leaves require unsupported copy snapshots");
6840 carriedPods.push_back(std::move(pod));
6842 if (carriedPods.empty()) {
6847 if (whileOp.getInits().size() != whileOp.getNumResults() ||
6848 !llvm::equal(whileOp.getInits().getTypes(), whileOp.getResultTypes())) {
6849 return reject(
"while initializer and result types must match");
6852 Block &beforeBody = *whileOp.getBeforeBody();
6853 Block &afterBody = *whileOp.getAfterBody();
6854 llvm::DenseSet<Value> uniquePodInits;
6855 for (
const WhileCarriedPod &pod : carriedPods) {
6856 if (hasUnsupportedWhileCarriedPodUse(
6857 whileOp.getBeforeArguments()[pod.originalIndex], pod, reason
6859 hasUnsupportedWhileCarriedPodUse(
6860 whileOp.getAfterArguments()[pod.originalIndex], pod, reason
6865 Value init = whileOp.getInits()[pod.originalIndex];
6868 if (!uniquePodInits.insert(init).second) {
6869 return reject(
"multiple carried POD arguments have the same initializer");
6871 if (!hasSupportedWhilePodInitialization(whileOp, init, reason)) {
6874 for (Operation *user : init.getUsers()) {
6875 if (user != whileOp.getOperation() &&
6876 (user->getBlock() != whileOp->getBlock() || !user->isBeforeInBlock(whileOp))) {
6877 return reject(
"POD initializer remains observable at or after the loop");
6882 auto terminatorForwardsCarriedPods = [&](ValueRange values, Block &body) {
6883 for (
const WhileCarriedPod &pod : carriedPods) {
6884 if (pod.originalIndex >= values.size() ||
6885 values[pod.originalIndex] != body.getArgument(pod.originalIndex)) {
6891 auto conditionTerminator = mlir::cast<scf::ConditionOp>(beforeBody.getTerminator());
6892 auto yieldTerminator = mlir::cast<scf::YieldOp>(afterBody.getTerminator());
6893 if (!terminatorForwardsCarriedPods(conditionTerminator.getArgs(), beforeBody) ||
6894 !terminatorForwardsCarriedPods(yieldTerminator.getOperands(), afterBody)) {
6896 "while terminators must forward each carried POD unchanged in its original position"
6904struct WhileCarriedPodRejection {
6914class WhileCarriedPodRejectionTracker {
6917 void record(Location loc, StringRef reason) {
6918 if (!reason.empty() && llvm::none_of(rejections, [&](
const auto &r) {
6919 return r.loc == loc && r.reason == reason;
6921 rejections.push_back({loc, reason.str()});
6926 ArrayRef<WhileCarriedPodRejection> getRejections()
const {
return rejections; }
6929 void clear() { rejections.clear(); }
6932 SmallVector<WhileCarriedPodRejection> rejections;
6936class SplitPodCarriedWhileValuesPattern final :
public OpRewritePattern<scf::WhileOp> {
6938 SplitPodCarriedWhileValuesPattern(MLIRContext *ctx, WhileCarriedPodRejectionTracker &tracker)
6939 : OpRewritePattern<scf::WhileOp>(ctx), rejectionTracker(tracker) {}
6941 LogicalResult matchAndRewrite(scf::WhileOp whileOp, PatternRewriter &rewriter)
const override {
6942 SmallVector<WhileCarriedPod> carriedPods;
6944 if (failed(checkWhileCarriedPods(whileOp, carriedPods, reason))) {
6945 rejectionTracker.record(whileOp.getLoc(), reason);
6946 return rewriter.notifyMatchFailure(whileOp, reason);
6948 Block &beforeBody = *whileOp.getBeforeBody();
6949 Block &afterBody = *whileOp.getAfterBody();
6951 auto lookupPod = [&](
unsigned originalIndex) ->
const WhileCarriedPod * {
6952 auto *it = llvm::find_if(carriedPods, [originalIndex](
const WhileCarriedPod &pod) {
6953 return pod.originalIndex == originalIndex;
6955 return it == carriedPods.end() ? nullptr : &*it;
6958 Location loc = whileOp.getLoc();
6959 SmallVector<Value> newInits;
6960 SmallVector<Type> newResultTypes;
6961 rewriter.setInsertionPoint(whileOp);
6962 for (
auto [idx, init] : llvm::enumerate(whileOp.getInits())) {
6965 newInits.push_back(init);
6966 newResultTypes.push_back(init.getType());
6969 for (
auto [path, type] : llvm::zip_equal(pod->leafPaths, pod->leafTypes)) {
6970 newInits.push_back(genReadAlongPath(rewriter, loc, init, path));
6971 newResultTypes.push_back(type);
6975 auto newWhile = scf::WhileOp::create(rewriter, loc, newResultTypes, newInits,
nullptr,
nullptr);
6976 newWhile->setAttrs(whileOp->getAttrs());
6977 Block &newBeforeBody = *newWhile.getBeforeBody();
6978 Block &newAfterBody = *newWhile.getAfterBody();
6979 dropTerminatorIfPresent(newBeforeBody);
6980 dropTerminatorIfPresent(newAfterBody);
6982 auto initializeMappingAndPodValues = [&](Block &oldBlock, Block &newBlock, IRMapping &mapping,
6983 SmallVector<SmallVector<Value>> &podValues) {
6984 unsigned newIdx = 0;
6985 for (
unsigned oldIdx = 0; oldIdx < oldBlock.getNumArguments(); ++oldIdx) {
6986 const WhileCarriedPod *pod = lookupPod(oldIdx);
6988 mapping.map(oldBlock.getArgument(oldIdx), newBlock.getArgument(newIdx++));
6991 SmallVector<Value> values;
6992 for (
size_t leafIdx = 0; leafIdx < pod->leafPaths.size(); ++leafIdx) {
6993 values.push_back(newBlock.getArgument(newIdx++));
6995 podValues.push_back(std::move(values));
6999 auto expandTerminatorValues = [&](ValueRange oldValues, IRMapping &mapping,
7000 ArrayRef<SmallVector<Value>> podValues) {
7001 SmallVector<Value> values;
7002 unsigned podIdx = 0;
7003 for (
auto [idx, oldValue] : llvm::enumerate(oldValues)) {
7005 llvm::append_range(values, podValues[podIdx++]);
7007 values.push_back(mapping.lookupOrDefault(oldValue));
7013 IRMapping beforeMapping;
7014 SmallVector<SmallVector<Value>> beforePodValues;
7015 initializeMappingAndPodValues(beforeBody, newBeforeBody, beforeMapping, beforePodValues);
7016 rewriter.setInsertionPointToEnd(&newBeforeBody);
7017 cloneWhileBodyWithCarriedPods(
7018 beforeBody, rewriter, beforeMapping, carriedPods, beforePodValues, [&](Operation &op) {
7019 auto conditionOp = dyn_cast<scf::ConditionOp>(&op);
7023 SmallVector<Value> args =
7024 expandTerminatorValues(conditionOp.getArgs(), beforeMapping, beforePodValues);
7026 conditionOp, scf::ConditionOp::create(
7027 rewriter, conditionOp.getLoc(),
7028 beforeMapping.lookupOrDefault(conditionOp.getCondition()), args
7035 IRMapping afterMapping;
7036 SmallVector<SmallVector<Value>> afterPodValues;
7037 initializeMappingAndPodValues(afterBody, newAfterBody, afterMapping, afterPodValues);
7038 rewriter.setInsertionPointToEnd(&newAfterBody);
7039 cloneWhileBodyWithCarriedPods(
7040 afterBody, rewriter, afterMapping, carriedPods, afterPodValues, [&](Operation &op) {
7041 auto yieldOp = dyn_cast<scf::YieldOp>(&op);
7045 SmallVector<Value> args =
7046 expandTerminatorValues(yieldOp.getOperands(), afterMapping, afterPodValues);
7052 rewriter.setInsertionPointAfter(newWhile);
7053 SmallVector<Value> replacements;
7054 unsigned newResultIdx = 0;
7055 for (
unsigned oldIdx = 0; oldIdx < whileOp.getNumResults(); ++oldIdx) {
7056 const WhileCarriedPod *pod = lookupPod(oldIdx);
7058 replacements.push_back(newWhile.getResult(newResultIdx++));
7063 VirtualPodLeafMap leafValues;
7064 for (
const RecordChain &path : pod->leafPaths) {
7065 leafValues[path] = newWhile.getResult(newResultIdx++);
7067 SmallVector<StringAttr> recordChain;
7068 for (RecordAttr record : pod->type.
getRecords()) {
7069 recordChain.push_back(record.getName());
7071 rebuildFlattenedPodRecord(rewriter, loc, record.getType(), recordChain, leafValues);
7072 genWrite(rewriter, loc, rebuilt, record.getName(), recordValue);
7073 recordChain.pop_back();
7075 replacements.push_back(rebuilt);
7078 rewriter.replaceOp(whileOp, replacements);
7083 WhileCarriedPodRejectionTracker &rejectionTracker;
7091class SplitPodInEmitEqualityPattern final :
public OpRewritePattern<constrain::EmitEqualityOp> {
7092 CompatiblePodLeafMaterializationMap &materializedLeaves;
7095 SplitPodInEmitEqualityPattern(
7096 MLIRContext *ctx, CompatiblePodLeafMaterializationMap &materializedLeafMap
7098 : OpRewritePattern<constrain::EmitEqualityOp>(ctx), materializedLeaves(materializedLeafMap) {}
7101 matchAndRewrite(constrain::EmitEqualityOp op, PatternRewriter &rewriter)
const override {
7102 PodType podTy = getWholePodEqualityType(op);
7107 return splitWholePodEmitEquality(op, rewriter, materializedLeaves);
7113applyGreedily(ModuleOp modOp, RewritePatternSet &&patterns,
bool *changed =
nullptr) {
7114 return applyPatternsGreedily(
7115 modOp->getRegion(0), std::move(patterns),
7116 GreedyRewriteConfig().enableFolding(
false).enableConstantCSE(
false), changed
7122static LogicalResult step4(ModuleOp modOp, WhileCarriedPodRejectionTracker &rejectionTracker) {
7123 CompatiblePodLeafMaterializationMap materializedLeaves;
7124 RewritePatternSet patterns(modOp.getContext());
7126 FoldReadAfterWriteInBlockPattern, ReplaceIfReadPattern, LiftPodWritesFromIfBlocksPattern,
7127 LiftPodAccessesFromForLoopPattern, LiftPodAccessesFromWhileLoopPattern,
7128 FoldIfCarriedPodReadAfterWritePattern>(patterns.getContext());
7129 patterns.add<SplitPodCarriedWhileValuesPattern>(patterns.getContext(), rejectionTracker);
7130 patterns.add<SplitPodInEmitEqualityPattern>(patterns.getContext(), materializedLeaves);
7132 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 4: refactor pod ops within SCF regions\n";);
7133 return applyGreedily(modOp, std::move(patterns));
7138static bool applyIfCarriedPodReadAfterWritePatterns(ModuleOp modOp) {
7139 RewritePatternSet patterns(modOp.getContext());
7140 patterns.add<FoldIfCarriedPodReadAfterWritePattern>(patterns.getContext());
7142 bool changed =
false;
7143 if (failed(applyGreedily(modOp, std::move(patterns), &changed))) {
7151static size_t podTypeScalarizationWeight(Type type) {
7152 auto podTy = dyn_cast<PodType>(type);
7158 for (RecordAttr record : podTy.
getRecords()) {
7159 weight += podTypeScalarizationWeight(record.getType());
7167static size_t podAllocScalarizationWeight(ModuleOp modOp) {
7169 modOp.walk([&weight](NewPodOp newPodOp) {
7170 weight += podTypeScalarizationWeight(newPodOp.getType());
7176inline static bool isResidualPodLikeType(Type type) {
7177 return splittablePod(type) || splittablePodArray(type);
7181template <
typename TypeRangeLike>
7182static size_t countResidualPodLikeTypes(
const TypeRangeLike &types) {
7184 for (Type type : types) {
7185 if (isResidualPodLikeType(type)) {
7193static bool isResidualPodPlaceholderCast(UnrealizedConversionCastOp castOp) {
7194 return countResidualPodLikeTypes(castOp.getOperandTypes()) != 0 ||
7195 countResidualPodLikeTypes(castOp.getResultTypes()) != 0;
7199static size_t countResidualPodIR(ModuleOp modOp) {
7201 modOp.walk([&count](Operation *op) {
7202 if (isa<NewPodOp, ReadPodOp, WritePodOp>(op)) {
7205 auto castOp = dyn_cast<UnrealizedConversionCastOp>(op);
7206 castOp && isResidualPodPlaceholderCast(castOp)
7211 count += countResidualPodLikeTypes(op->getOperandTypes());
7212 count += countResidualPodLikeTypes(op->getResultTypes());
7214 for (Region ®ion : op->getRegions()) {
7215 for (Block &block : region) {
7216 count += countResidualPodLikeTypes(block.getArgumentTypes());
7220 if (
auto funcDef = dyn_cast<FuncDefOp>(op)) {
7221 FunctionType funcTy = funcDef.getFunctionType();
7222 count += countResidualPodLikeTypes(funcTy.getInputs());
7223 count += countResidualPodLikeTypes(funcTy.getResults());
7224 }
else if (
auto memberDef = dyn_cast<MemberDefOp>(op)) {
7225 count += isResidualPodLikeType(memberDef.getType()) ? 1 : 0;
7232static LogicalResult rejectRaggedNestedLeafArrayLengths(ModuleOp modOp) {
7233 auto result = modOp.walk([](ArrayLengthOp op) -> WalkResult {
7234 StringRef raggedKind = getTaggedRaggedNestedLeafKind(op.
getArrRef());
7235 if (raggedKind.empty()) {
7236 return WalkResult::advance();
7238 return op.emitOpError() <<
"cannot lower nested " << raggedKind
7239 <<
" array leaf length after reading an array-of-POD element "
7240 "without per-element shape witnesses";
7242 return failure(result.wasInterrupted());
7246static LogicalResult rejectRaggedNestedLeafArrayEqualities(ModuleOp modOp) {
7247 auto result = modOp.walk([](constrain::EmitEqualityOp op) -> WalkResult {
7248 StringRef raggedKind = getTaggedRaggedNestedLeafKind(op.
getLhs());
7249 if (raggedKind.empty()) {
7250 raggedKind = getTaggedRaggedNestedLeafKind(op.
getRhs());
7252 if (raggedKind.empty()) {
7253 return WalkResult::advance();
7255 return op.emitOpError() <<
"cannot lower nested " << raggedKind
7256 <<
" array leaf equality after reading an array-of-POD element "
7257 "without per-element shape witnesses";
7259 return failure(result.wasInterrupted());
7263static LogicalResult rejectRaggedNestedLeafArrayContainments(ModuleOp modOp) {
7264 auto result = modOp.walk([](constrain::EmitContainmentOp op) -> WalkResult {
7265 return rejectRaggedNestedLeafContainment(op);
7267 return failure(result.wasInterrupted());
7271static LogicalResult rejectRaggedNestedLeafBoundaryCrossings(ModuleOp modOp) {
7272 auto result = modOp.walk([](Operation *op) -> WalkResult {
7273 return TypeSwitch<Operation *, LogicalResult>(op)
7274 .Case<CallOp>([](CallOp callOp) {
7275 return rejectRaggedNestedLeafBoundaryCrossing(
7276 callOp.getOperation(), callOp.getArgOperands(),
"across a function call"
7279 .Case<ReturnOp>([](ReturnOp returnOp) {
7280 return rejectRaggedNestedLeafBoundaryCrossing(
7281 returnOp.getOperation(), returnOp.
getOperands(),
"across a function return"
7284 .Case<scf::ForOp>([](scf::ForOp forOp) {
7285 return rejectRaggedNestedLeafBoundaryCrossing(
7286 forOp.getOperation(), forOp.getInitArgs(),
"into an scf.for region"
7289 .Case<scf::WhileOp>([](scf::WhileOp whileOp) {
7290 return rejectRaggedNestedLeafBoundaryCrossing(
7291 whileOp.getOperation(), whileOp.getInits(),
"into an scf.while region"
7294 .Case<scf::ConditionOp>([](scf::ConditionOp conditionOp) {
7295 return rejectRaggedNestedLeafBoundaryCrossing(
7296 conditionOp.getOperation(), conditionOp.getArgs(),
"across an scf.condition boundary"
7299 .Case<scf::YieldOp>([](scf::YieldOp yieldOp) {
7300 return rejectRaggedNestedLeafBoundaryCrossing(
7301 yieldOp.getOperation(), yieldOp.getOperands(),
"across an scf.yield boundary"
7303 }).Default([](Operation *) {
return success(); });
7305 return failure(result.wasInterrupted());
7309static LogicalResult rejectRemainingRaggedNestedLeafUses(ModuleOp modOp) {
7310 if (failed(rejectRaggedNestedLeafArrayLengths(modOp))) {
7313 if (failed(rejectRaggedNestedLeafArrayEqualities(modOp))) {
7316 if (failed(rejectRaggedNestedLeafArrayContainments(modOp))) {
7319 return rejectRaggedNestedLeafBoundaryCrossings(modOp);
7323class PassImpl :
public llzk::pod::impl::PodToScalarPassBase<PassImpl> {
7324 using Base = PodToScalarPassBase<PassImpl>;
7327 WhileCarriedPodRejectionTracker whileCarriedPodRejections;
7330 static OpPassManager createScalarizePipeline() {
7331 OpPassManager pm(ModuleOp::getOperationName());
7342 static OpPassManager createCleanupPipeline() {
7343 OpPassManager pm(ModuleOp::getOperationName());
7346 RemoveUnusedDiscardableAllocationsPassOptions {
7351 RemoveUnusedDiscardableAllocationsPassOptions {
7359 void getDependentDialects(DialectRegistry ®istry)
const override {
7360 OpPassManager scalarizePM = createScalarizePipeline();
7361 scalarizePM.getDependentDialects(registry);
7363 OpPassManager cleanupPM = createCleanupPipeline();
7364 cleanupPM.getDependentDialects(registry);
7367 LogicalResult runScalarizeAndCleanupPipeline(ModuleOp module) {
7368 OpPassManager scalarizePM = createScalarizePipeline();
7369 OpPassManager cleanupPM = createCleanupPipeline();
7371 size_t podAllocWeight = podAllocScalarizationWeight(module);
7372 while (podAllocWeight != 0) {
7373 if (failed(runPipeline(scalarizePM, module))) {
7380 bool foldedIfCarriedRead = applyIfCarriedPodReadAfterWritePatterns(module);
7381 if (failed(runPipeline(cleanupPM, module))) {
7388 size_t nextPodAllocWeight = podAllocScalarizationWeight(module);
7389 if (!foldedIfCarriedRead && nextPodAllocWeight == podAllocWeight) {
7392 podAllocWeight = nextPodAllocWeight;
7398 void runOnOperation()
override {
7399 whileCarriedPodRejections.clear();
7401 ModuleOp module = getOperation();
7402 if (failed(step0(module))) {
7403 return signalPassFailure();
7406 llvm::dbgs() <<
"After step 0:\n";
7410 size_t previousResidualCount = std::numeric_limits<size_t>::max();
7416 SymbolTableCollection symTables;
7417 MemberReplacementMap memberRepMap;
7418 if (failed(step1(module, symTables, memberRepMap))) {
7419 return signalPassFailure();
7422 llvm::dbgs() <<
"After step 1:\n";
7426 materializeFreshPodArrayMutationSnapshots(module);
7427 if (failed(rejectUnsupportedPodReadArrayMutations(module))) {
7428 return signalPassFailure();
7431 if (failed(step2(module, symTables, memberRepMap))) {
7432 return signalPassFailure();
7435 llvm::dbgs() <<
"After step 2:\n";
7439 if (failed(rejectUnsupportedRaggedSnapshots(module)) ||
7440 failed(step3(module, symTables, memberRepMap))) {
7441 return signalPassFailure();
7444 llvm::dbgs() <<
"After step 3:\n";
7448 if (failed(rejectRemainingRaggedNestedLeafUses(module))) {
7449 signalPassFailure();
7453 if (failed(step4(module, whileCarriedPodRejections))) {
7454 return signalPassFailure();
7457 llvm::dbgs() <<
"After step 4:\n";
7461 if (failed(runScalarizeAndCleanupPipeline(module))) {
7462 signalPassFailure();
7466 llvm::dbgs() <<
"After SROA+Mem2Reg pipeline:\n";
7470 if (failed(rejectRemainingRaggedNestedLeafUses(module))) {
7471 signalPassFailure();
7475 size_t residualCount = countResidualPodIR(module);
7476 if (residualCount == 0) {
7479 if (residualCount >= previousResidualCount) {
7480 std::string residualIR;
7481 llvm::raw_string_ostream os(residualIR);
7483 auto diagnostic =
module.emitError();
7484 diagnostic <<
"llzk-pod-to-scalar left residual pod IR after reaching a fixpoint ("
7485 << residualCount <<
" residual pod-like items remain)\n"
7487 for (
const WhileCarriedPodRejection &r : whileCarriedPodRejections.getRejections()) {
7488 diagnostic.attachNote(r.loc) <<
"cannot scalarize carried POD: " << r.reason;
7490 module.walk([&diagnostic](NewPodOp newPod) {
7491 ArrayRef<RecordAttr> records = newPod.getType().getRecords();
7492 Type recordType = records.size() == 1 ? records.front().getType() : Type {};
7494 diagnostic.attachNote(newPod.getLoc())
7495 <<
"cannot promote POD record '" << records.front().getName().getValue()
7499 llvm::DenseSet<Operation *> diagnosedReads;
7502 VirtualPodValueMap virtualPods;
7503 rehydrateVirtualPodPlaceholders(module, virtualPods);
7504 module.walk([&diagnostic, &diagnosedReads, &virtualPods](ReadPodOp readOp) {
7505 Type recordType = readOp.getType();
7506 if (!lookupVirtualPodLeafMap(readOp.
getPodRef(), virtualPods) ||
7511 diagnosedReads.insert(readOp.getOperation());
7512 diagnostic.attachNote(readOp.getLoc())
7513 <<
"cannot lower value-copy read of POD record '" << readOp.
getRecordName()
7516 module.walk([&diagnostic, &diagnosedReads](UnrealizedConversionCastOp castOp) {
7518 SmallVector<RecordChain> splitIds;
7519 SmallVector<Type> splitTypes;
7520 if (!getDeferredSplitPodArrayCastInfo(castOp, arrTy, splitIds, splitTypes)) {
7524 ReadPodOp readOp = peelUnifiableCasts(castOp.getOperand(0)).getDefiningOp<ReadPodOp>();
7526 !diagnosedReads.insert(readOp.getOperation()).second) {
7530 diagnostic.attachNote(readOp.getLoc())
7531 <<
"cannot lower value-copy read of POD record '" << readOp.
getRecordName()
7534 diagnostic.report();
7535 signalPassFailure();
7538 previousResidualCount = residualCount;
within a display generated by the Derivative if and wherever such third party notices normally appear The contents of the NOTICE file are for informational purposes only and do not modify the License You may add Your own attribution notices within Derivative Works that You alongside or as an addendum to the NOTICE text from the provided that such additional attribution notices cannot be construed as modifying the License You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for or distribution of Your or for any such Derivative Works as a provided Your and distribution of the Work otherwise complies with the conditions stated in this License Submission of Contributions Unless You explicitly state any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this without any additional terms or conditions Notwithstanding the nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions Trademarks This License does not grant permission to use the trade names
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
Provides SpecializedSROA<AllocOpTy> and SpecializedMem2Reg<AllocOpTy>: pass templates that replicate ...
static NonDetOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type res)
::mlir::Value genRead(::mlir::OpBuilder &bldr, ::mlir::Location loc, ::mlir::Value arrayRef, ::mlir::ValueRange indices)
Create an array.read or array.extract for one concrete element or subarray.
static ::llvm::SmallVector<::mlir::Value > genIndexConstants(::mlir::OpBuilder &bldr, ::mlir::Location loc, ::mlir::ArrayAttr index)
Generate arith.constant indices for one static array element position.
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of the referenced base array.
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
static ArrayLengthOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type length, ::mlir::Value arr_ref, ::mlir::Value dim)
::mlir::TypedValue<::mlir::IndexType > getDim()
ArrayType cloneWith(std::optional<::llvm::ArrayRef< int64_t > > shape, ::mlir::Type elementType) const
Clone this type with the given shape and element type.
std::optional<::llvm::SmallVector<::mlir::ArrayAttr > > getSubelementIndices() const
Return a list of all valid indices for this ArrayType.
::mlir::Type getElementType() const
::mlir::Type getSelectionType(size_t numIndices) const
Return the type produced by selecting/removing numIndices leading dimensions.
static ArrayType get(::mlir::Type elementType, ::llvm::ArrayRef<::mlir::Attribute > dimensionSizes)
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
static CreateArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::llzk::array::ArrayType result, ::mlir::ValueRange elements={})
::mlir::TypedValue<::llzk::array::ArrayType > getResult()
::mlir::DenseI32ArrayAttr getNumDimsPerMapAttr()
static constexpr ::llvm::StringLiteral getOperationName()
::mlir::Operation::operand_range getElements()
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of the referenced base array.
::mlir::TypedValue<::llzk::array::ArrayType > getRvalue()
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
static InsertArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value arr_ref, ::mlir::ValueRange indices, ::mlir::Value rvalue)
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of the referenced base array.
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
static ReadArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::Value arr_ref, ::mlir::ValueRange indices)
static WriteArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value arr_ref, ::mlir::ValueRange indices, ::mlir::Value rvalue)
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of the referenced base array.
LLZK type conversion operations.
bool hasPublicAttr()
Returns whether this member is a public output.
void setPublicAttr(bool newValue=true)
Adds or removes the unit llzk.pub attribute according to newValue.
::mlir::Attribute removeSignalAttr()
::mlir::StringAttr getSymNameAttr()
void setType(::mlir::Type attrValue)
static MemberDefOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::StringAttr sym_name, ::mlir::TypeAttr type, bool isSignal=false, bool isColumn=false)
::mlir::TypedValue<::llzk::component::StructType > getComponent()
static MemberReadOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type resultType, ::mlir::Value component, ::mlir::StringAttr member)
::std::optional<::mlir::Attribute > getTableOffset()
::mlir::FlatSymbolRefAttr getMemberNameAttr()
::llvm::ArrayRef< int32_t > getNumDimsPerMap()
::mlir::TypedValue<::llzk::component::StructType > getComponent()
::mlir::FlatSymbolRefAttr getMemberNameAttr()
static MemberWriteOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value component, ::mlir::FlatSymbolRefAttr member_name, ::mlir::Value val)
::mlir::TypedValue<::mlir::Type > getVal()
LLZK component/struct operations.
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
::mlir::ArrayAttr getParams() const
LLZK constraint emission operations.
::mlir::TypedValue<::mlir::Type > getRhs()
::mlir::TypedValue<::llzk::array::ArrayType > getLhs()
::mlir::TypedValue<::mlir::Type > getLhs()
static EmitEqualityOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value lhs, ::mlir::Value rhs)
::mlir::TypedValue<::mlir::Type > getRhs()
LLZK felt (Field ELemenT) type and operations.
::llvm::SmallVector< RangeT > getMapOperands()
::mlir::MutableOperandRange getArgOperandsMutable()
::mlir::Operation::operand_range getArgOperands()
::mlir::FunctionType getFunctionType()
void setArgAttrsAttr(::mlir::ArrayAttr attr)
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
void setResAttrsAttr(::mlir::ArrayAttr attr)
::mlir::ArrayAttr getArgAttrsAttr()
void setFunctionType(::mlir::FunctionType attrValue)
::llvm::ArrayRef<::mlir::Type > getResultTypes()
Required by FunctionOpInterface.
::mlir::Region & getBody()
::mlir::ArrayAttr getResAttrsAttr()
LLZK function operations.
::mlir::MutableOperandRange getOperandsMutable()
::mlir::Operation::operand_range getOperands()
LLZK support for global values, defined outside of structs.
static NewPodOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::llzk::pod::InitializedRecords initialValues={})
::mlir::Operation::operand_range getInitialValues()
::mlir::OperandRangeRange getMapOperands()
::llvm::ArrayRef< int32_t > getNumDimsPerMap()
::mlir::TypedValue<::llzk::pod::PodType > getResult()
void setInitializedRecordsAttr(::mlir::ArrayAttr attr)
::mlir::MutableOperandRange getInitialValuesMutable()
::mlir::ArrayAttr getInitializedRecords()
static constexpr ::llvm::StringLiteral getOperationName()
Dialect for working with plain-old-data structs.
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
::llvm::StringRef getRecordName()
::mlir::TypedValue<::mlir::Type > getResult()
inline ::llzk::pod::PodType getPodRefType()
Gets the type of the referenced pod.
::mlir::TypedValue<::llzk::pod::PodType > getPodRef()
::mlir::StringAttr getRecordNameAttr()
static ReadPodOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::Value pod_ref, ::mlir::StringAttr record_name)
inline ::llzk::pod::PodType getPodRefType()
Gets the type of the referenced pod.
::mlir::StringAttr getRecordNameAttr()
::mlir::TypedValue<::mlir::Type > getValue()
::mlir::TypedValue<::llzk::pod::PodType > getPodRef()
::llvm::StringRef getRecordName()
static WritePodOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value pod_ref, ::mlir::StringAttr record_name, ::mlir::Value value)
LLZK types and operations to support templated/parameterized structs.
::mlir::TypedValue<::mlir::Type > getInput()
static UnifiableCastOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::Value input)
LLZK support for random access memory during witness generation.
LLZK string type and operations.
std::unique_ptr<::mlir::Pass > createLowerBoolQuantifiersPass()
constexpr char ARG_NAME_ATTR_NAME[]
Attribute name for source-level function argument names.
constexpr char RES_NAME_ATTR_NAME[]
Attribute name for source-level function result names.
bool appendValuesWithExactTypes(mlir::ValueRange values, mlir::TypeRange expectedTypes, llvm::SmallVectorImpl< mlir::Value > &output)
Append values only when every entry exactly matches the corresponding expected type.
mlir::Operation * findNearestLoopCarriedPodAccess(ReadPodOp readOp)
Return the nearest enclosing SCF loop that carries writes to the same external POD record.
std::unique_ptr<::mlir::Pass > createTypeVarInferencePass()
OpClass replaceOpWithNewOp(Rewriter &rewriter, mlir::Operation *op, Args &&...args)
Wrapper for RewriterBase::replaceOpWithNewOp() that automatically copies discardable attributes (i....
std::unique_ptr<::mlir::Pass > createRemoveUnusedDiscardableAllocationsPass()
std::unique_ptr< mlir::Pass > createRemoveDeadValuesWorkaroundPass()
mlir::ArrayAttr replicateFunctionNameAttrsAsNeeded(mlir::ArrayAttr origAttrs, const llvm::SmallVector< size_t > &originalIdxToSize, const llvm::SmallVector< mlir::Type > &newTypes, llvm::StringRef functionNameAttrName, llvm::ArrayRef< std::optional< llvm::StringRef > > origNames={}, llvm::ArrayRef< llvm::StringRef > existingNames={}, llvm::ArrayRef< llvm::SmallVector< std::string > > splitNameSuffixes={})
Expand function arg/result attribute arrays to match a split signature, rewriting name attrs with the...
bool isNullOrEmpty(mlir::ArrayAttr a)
mlir::DictionaryAttr withFunctionArgNameAttr(mlir::DictionaryAttr attrs, llvm::StringRef name)
Return a copy of the given argument attribute dictionary with function.arg_name set to name.
std::string getValueCopyFailureReason(Type type)
constexpr T checkedCast(U u) noexcept
bool requiresUnsupportedValueCopy(mlir::Type type)
Return whether type requires a value copy that cannot currently be materialized.
OpTy preserveDiscardableAttrs(mlir::Operation *src, OpTy dst)
Copy discardable attributes from src to dst.
FailureOr< Value > materializeValueCopy(OpBuilder &builder, Location loc, Value source)
ArrayType flattenArrayElementType(ArrayType outerArrTy, Type elementType)
std::unique_ptr< SpecializedMem2Reg< AllocOpTy > > createSpecializedMem2RegPass()
bool requiresValueCopy(Type type)
bool isDynamic(IntegerAttr intAttr)
bool typesUnify(Type lhs, Type rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
function::CallOp createCallPreservingInstantiationOperands(mlir::Location loc, mlir::TypeRange newResultTypes, function::CallOp oldCall, llvm::ArrayRef< mlir::ValueRange > mapOperands, mlir::ValueRange argOperands, mlir::ConversionPatternRewriter &rewriter)
Rebuild a function.call while preserving explicit instantiation state from oldCall.
SplitFunctionNameInfo collectSplitFunctionNameInfo(mlir::ArrayRef< mlir::Type > origTypes, GetNameAttrFn &&getNameAttr, GetSplitSuffixesFn &&getSplitSuffixes)
Collect function arg/result names and split suffixes from a list of original types.
bool hasAffineMapAttr(Type type)
mlir::Operation * create(MlirOpBuilder cBuilder, MlirLocation cLocation, Args &&...args)
Creates a new operation using an ODS build method.
std::unique_ptr< SpecializedSROA< AllocOpTy > > createSpecializedSROAPass()
bool canMaterializeValueCopy(Type type)
OpTy preserveDiscardableAttrsExcept(mlir::Operation *src, OpTy dst, mlir::StringRef excludedAttr)
Copy discardable attributes from src to dst while omitting one internal attr.
std::string reserveUniqueAttrName(llvm::StringSet<> &usedNames, llvm::StringRef desiredName)
Reserve and return a unique function argument/result name based on desiredName.
static unsigned getHashValue(const CompatiblePodLeafMaterializationKey &key)
static bool isEqual(const CompatiblePodLeafMaterializationKey &lhs, const CompatiblePodLeafMaterializationKey &rhs)
static unsigned getHashValue(const RecordChain &chain)
static bool isEqual(const RecordChain &lhs, const RecordChain &rhs)
llvm::SmallVector< std::optional< llvm::StringRef > > originalNames
llvm::SmallVector< llvm::StringRef > existingNames
llvm::SmallVector< llvm::SmallVector< std::string > > splitNameSuffixes