97#include <mlir/Dialect/Arith/IR/Arith.h>
98#include <mlir/Dialect/SCF/IR/SCF.h>
99#include <mlir/Dialect/UB/IR/UBOps.h>
100#include <mlir/IR/BuiltinOps.h>
101#include <mlir/Pass/PassManager.h>
102#include <mlir/Transforms/DialectConversion.h>
103#include <mlir/Transforms/Passes.h>
105#include <llvm/Support/Debug.h>
111#define GEN_PASS_DEF_ARRAYTOSCALARPASS
112#define GEN_PASS_DEF_STRAIGHTLINESTATICARRAYPROMOTIONPASS
123#define DEBUG_TYPE "llzk-array-to-scalar"
129 return at.hasStaticShape() && !llvm::isa<NoneType>(at.
getElementType()) ? at :
nullptr;
134 if (
ArrayType at = dyn_cast<ArrayType>(t)) {
135 return splittableArray(at);
143inline bool containsSplittableArrayType(ArrayRef<Type> types) {
144 for (Type t : types) {
145 if (splittableArray(t)) {
153template <
typename T>
bool containsSplittableArrayType(ValueTypeRange<T> types) {
154 for (Type t : types) {
155 if (splittableArray(t)) {
164size_t splitArrayTypeTo(Type t, SmallVector<Type> &collect) {
167 collect.append(size, at.getElementType());
170 collect.push_back(t);
176template <
typename TypeCollection>
177inline void splitArrayTypeTo(
178 TypeCollection types, SmallVector<Type> &collect, SmallVector<size_t> *originalIdxToSize
180 for (Type t : types) {
181 size_t count = splitArrayTypeTo(t, collect);
182 if (originalIdxToSize) {
183 originalIdxToSize->push_back(count);
190template <
typename TypeCollection>
191inline SmallVector<Type>
192splitArrayType(TypeCollection types, SmallVector<size_t> *originalIdxToSize =
nullptr) {
193 SmallVector<Type> collect;
194 splitArrayTypeTo(types, collect, originalIdxToSize);
199static std::string formatSplitArrayIndexSuffix(ArrayAttr index) {
201 llvm::raw_string_ostream os(suffix);
202 for (Attribute attr : index) {
204 attr.print(os,
true);
211static SmallVector<std::string> getSplitArrayIndexSuffixes(Type type) {
212 SmallVector<std::string> suffixes;
213 if (
ArrayType at = splittableArray(type)) {
214 std::optional<SmallVector<ArrayAttr>> indices = at.getSubelementIndices();
215 assert(indices.has_value() &&
"static-shape arrays must provide subelement indices");
216 suffixes.reserve(indices->size());
217 for (ArrayAttr index : *indices) {
218 suffixes.push_back(formatSplitArrayIndexSuffix(index));
225CallOp newCallOpWithSplitResults(
228 OpBuilder::InsertionGuard guard(rewriter);
229 rewriter.setInsertionPointAfter(oldCall);
231 Operation::result_range oldResults = oldCall.getResults();
233 oldCall.getLoc(), splitArrayType(oldResults.getTypes()), oldCall, adaptor.
getMapOperands(),
237 auto newResults = newCall.getResults().begin();
238 SmallVector<Value> replacements;
239 replacements.reserve(oldResults.size());
240 for (Value oldVal : oldResults) {
241 if (
ArrayType at = splittableArray(oldVal.getType())) {
242 Location loc = oldVal.getLoc();
245 replacements.push_back(newArray);
249 std::optional<SmallVector<ArrayAttr>> allIndices = at.getSubelementIndices();
251 assert(std::cmp_equal(allIndices->size(), at.getNumElements()));
252 for (ArrayAttr subIdx : allIndices.value()) {
257 replacements.push_back(*newResults);
264 rewriter.replaceOp(oldCall, replacements);
271void processInputOperand(
272 Location loc, Value operand, SmallVector<Value> &newOperands,
273 ConversionPatternRewriter &rewriter
275 if (
ArrayType at = splittableArray(operand.getType())) {
276 std::optional<SmallVector<ArrayAttr>> indices = at.getSubelementIndices();
277 assert(indices.has_value() &&
"passed earlier hasStaticShape() check");
278 for (ArrayAttr index : indices.value()) {
282 newOperands.push_back(operand);
287void processInputOperands(
288 ValueRange operands, MutableOperandRange outputOpRef, Operation *op,
289 ConversionPatternRewriter &rewriter
291 SmallVector<Value> newOperands;
292 for (Value v : operands) {
293 processInputOperand(op->getLoc(), v, newOperands, rewriter);
295 rewriter.modifyOpInPlace(op, [&outputOpRef, &newOperands]() {
296 outputOpRef.assign(ValueRange(newOperands));
302enum Direction : std::uint8_t {
311template <Direction dir>
312inline void rewriteImpl(
314 ConversionPatternRewriter &rewriter
317 Location loc = op.getLoc();
318 MLIRContext *ctx = op.getContext();
327 assert(std::cmp_equal(subIndices->size(), smallType.getNumElements()));
328 for (ArrayAttr indexingTail : subIndices.value()) {
329 SmallVector<Attribute> joined;
330 joined.append(indexAsAttr.begin(), indexAsAttr.end());
331 joined.append(indexingTail.begin(), indexingTail.end());
332 ArrayAttr fullIndex = ArrayAttr::get(ctx, joined);
334 if constexpr (dir == Direction::SMALL_TO_LARGE) {
337 }
else if constexpr (dir == Direction::LARGE_TO_SMALL) {
347class SplitInsertArrayOp :
public OpConversionPattern<InsertArrayOp> {
349 using OpConversionPattern<InsertArrayOp>::OpConversionPattern;
351 static bool legal(InsertArrayOp op) {
352 return !containsSplittableArrayType(op.
getRvalue().getType());
355 LogicalResult matchAndRewrite(
356 InsertArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
361 ArrayType at = splittableArray(op.
getRvalue().getType());
362 rewriteImpl<SMALL_TO_LARGE>(
363 llvm::cast<ArrayAccessOpInterface>(op.getOperation()), at, adaptor.getRvalue(),
364 adaptor.getArrRef(), rewriter
366 rewriter.eraseOp(op);
372class SplitExtractArrayOp :
public OpConversionPattern<ExtractArrayOp> {
374 using OpConversionPattern<ExtractArrayOp>::OpConversionPattern;
376 static bool legal(ExtractArrayOp op) {
377 return !containsSplittableArrayType(op.
getResult().getType());
380 LogicalResult matchAndRewrite(
381 ExtractArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
386 ArrayType at = splittableArray(op.
getResult().getType());
389 rewriteImpl<LARGE_TO_SMALL>(
390 llvm::cast<ArrayAccessOpInterface>(op.getOperation()), at, newArray, adaptor.getArrRef(),
398class SplitInitFromCreateArrayOp :
public OpConversionPattern<CreateArrayOp> {
400 using OpConversionPattern<CreateArrayOp>::OpConversionPattern;
402 static bool legal(CreateArrayOp op) {
return op.
getElements().empty(); }
404 LogicalResult matchAndRewrite(
405 CreateArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
413 rewriter.setInsertionPointAfter(op);
414 Location loc = op.getLoc();
416 for (
auto [i, init] : llvm::enumerate(adaptor.getElements())) {
418 std::optional<SmallVector<Value>> multiDimIdxVals =
422 assert(multiDimIdxVals.has_value());
431class SplitArrayInFuncDefOp :
public OpConversionPattern<FuncDefOp> {
433 using OpConversionPattern<FuncDefOp>::OpConversionPattern;
435 inline static bool legal(FuncDefOp op) {
441 matchAndRewrite(FuncDefOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
446 class Impl :
public FunctionTypeConverter {
447 SmallVector<size_t> originalInputIdxToSize, originalResultIdxToSize;
448 SplitFunctionNameInfo inputNameInfo;
449 SplitFunctionNameInfo resultNameInfo;
452 SmallVector<Type> convertInputs(ArrayRef<Type> origTypes)
override {
453 return splitArrayType(origTypes, &originalInputIdxToSize);
455 SmallVector<Type> convertResults(ArrayRef<Type> origTypes)
override {
456 return splitArrayType(origTypes, &originalResultIdxToSize);
458 ArrayAttr convertInputAttrs(ArrayAttr origAttrs, SmallVector<Type> newTypes)
override {
465 ArrayAttr convertResultAttrs(ArrayAttr origAttrs, SmallVector<Type> newTypes)
override {
477 void processBlockArgs(Block &entryBlock, RewriterBase &rewriter)
override {
478 OpBuilder::InsertionGuard guard(rewriter);
479 rewriter.setInsertionPointToStart(&entryBlock);
481 for (
unsigned i = 0; i < entryBlock.getNumArguments();) {
482 Value oldV = entryBlock.getArgument(i);
483 if (ArrayType at = splittableArray(oldV.getType())) {
484 Location loc = oldV.getLoc();
490 oldV.replaceAllUsesWith(newArray);
492 entryBlock.eraseArgument(i);
497 assert(std::cmp_equal(allIndices->size(), at.getNumElements()));
498 for (ArrayAttr subIdx : allIndices.value()) {
499 BlockArgument newArg = entryBlock.insertArgument(i, at.
getElementType(), loc);
511 ArrayAttr resultAttrs = op.getAllResultAttrs();
513 return op.getArgNameAttr(i);
514 }, getSplitArrayIndexSuffixes);
516 return getAttrAtIndexWithName(resultAttrs, i, RES_NAME_ATTR_NAME);
517 }, getSplitArrayIndexSuffixes);
520 Impl(op).convert(op, rewriter);
526class SplitArrayInReturnOp :
public OpConversionPattern<ReturnOp> {
528 using OpConversionPattern<ReturnOp>::OpConversionPattern;
530 inline static bool legal(ReturnOp op) {
531 return !containsSplittableArrayType(op.
getOperands().getTypes());
534 LogicalResult matchAndRewrite(
535 ReturnOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
546class SplitArrayInCallOp :
public OpConversionPattern<CallOp> {
548 using OpConversionPattern<CallOp>::OpConversionPattern;
550 inline static bool legal(CallOp op) {
551 return !containsSplittableArrayType(op.
getArgOperands().getTypes()) &&
552 !containsSplittableArrayType(op.getResultTypes());
555 LogicalResult matchAndRewrite(
556 CallOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
562 CallOp newCall = newCallOpWithSplitResults(op, adaptor, rewriter);
563 processInputOperands(
571class ReplaceKnownArrayLengthOp :
public OpConversionPattern<ArrayLengthOp> {
573 using OpConversionPattern<ArrayLengthOp>::OpConversionPattern;
576 static std::optional<llvm::APInt> getDimSizeIfKnown(Value dimIdx, ArrayType baseArrType) {
577 if (baseArrType.hasStaticShape()) {
579 if (mlir::matchPattern(dimIdx, mlir::m_ConstantInt(&idxAP))) {
580 std::optional<int64_t> signedIdx = idxAP.trySExtValue();
581 if (!signedIdx || *signedIdx < 0) {
586 if (idx >= dimSizes.size()) {
589 Attribute dimSizeAttr = dimSizes[idx];
590 if (mlir::matchPattern(dimSizeAttr, mlir::m_ConstantInt(&idxAP))) {
598 inline static bool legal(ArrayLengthOp op) {
603 LogicalResult matchAndRewrite(
604 ArrayLengthOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
609 ArrayType arrTy = dyn_cast<ArrayType>(adaptor.getArrRef().getType());
611 std::optional<llvm::APInt> len = getDimSizeIfKnown(adaptor.getDim(), arrTy);
612 assert(len.has_value());
619using MemberInfo = std::pair<StringAttr, Type>;
621using LocalMemberReplacementMap = DenseMap<ArrayAttr, MemberInfo>;
623using MemberReplacementMap = DenseMap<StructDefOp, DenseMap<StringAttr, LocalMemberReplacementMap>>;
626class SplitArrayInMemberDefOp :
public OpConversionPattern<MemberDefOp> {
627 SymbolTableCollection &tables;
628 MemberReplacementMap &repMapRef;
631 SplitArrayInMemberDefOp(
632 MLIRContext *ctx, SymbolTableCollection &symTables, MemberReplacementMap &memberRepMap
634 : OpConversionPattern<MemberDefOp>(ctx), tables(symTables), repMapRef(memberRepMap) {}
636 inline static bool legal(MemberDefOp op) {
return !containsSplittableArrayType(op.
getType()); }
639 matchAndRewrite(MemberDefOp op, OpAdaptor, ConversionPatternRewriter &rewriter)
const override {
643 StructDefOp inStruct = op->getParentOfType<StructDefOp>();
645 LocalMemberReplacementMap &localRepMapRef = repMapRef[inStruct][op.
getSymNameAttr()];
647 ArrayType arrTy = dyn_cast<ArrayType>(op.
getType());
650 assert(subIdxs.has_value());
653 SymbolTable &structSymbolTable = tables.getSymbolTable(inStruct);
654 for (ArrayAttr idx : subIdxs.value()) {
661 localRepMapRef[idx] = std::make_pair(structSymbolTable.insert(newMember), elemTy);
663 rewriter.eraseOp(op);
670 SplitArrayInMemberWriteOp, MemberWriteOp, void *, ArrayAttr> {
672 using SplitAggregateInMemberRefOp<
673 SplitArrayInMemberWriteOp, MemberWriteOp,
void *, ArrayAttr>::SplitAggregateInMemberRefOp;
675 static bool legal(MemberWriteOp op) {
676 return !containsSplittableArrayType(op.
getVal().getType());
679 static void *genHeader(MemberWriteOp, ConversionPatternRewriter &) {
return nullptr; }
682 Location loc,
void *, ArrayAttr idx, MemberInfo newMember, OpAdaptor adaptor,
683 ConversionPatternRewriter &rewriter
687 rewriter, loc, adaptor.getComponent(), FlatSymbolRefAttr::get(newMember.first), scalarRead
694class SplitArrayInMemberReadOp
696 SplitArrayInMemberReadOp, MemberReadOp, CreateArrayOp, ArrayAttr> {
698 using SplitAggregateInMemberRefOp<
699 SplitArrayInMemberReadOp, MemberReadOp, CreateArrayOp,
700 ArrayAttr>::SplitAggregateInMemberRefOp;
702 static bool legal(MemberReadOp op) {
703 return !containsSplittableArrayType(op.getResult().getType());
706 static CreateArrayOp genHeader(MemberReadOp op, ConversionPatternRewriter &rewriter) {
712 replacement(MemberReadOp, CreateArrayOp newArray, OpAdaptor, ConversionPatternRewriter &) {
717 Location loc, CreateArrayOp newArray, ArrayAttr idx, MemberInfo newMember, OpAdaptor adaptor,
718 ConversionPatternRewriter &rewriter
721 rewriter, loc, newMember.second, adaptor.getComponent(), newMember.first
728static void baseTargetSetup(ConversionTarget &target) {
729 target.addLegalDialect<
735 target.addLegalOp<ModuleOp>();
739class NondetToNewArray :
public OpConversionPattern<NonDetOp> {
740 using OpConversionPattern<NonDetOp>::OpConversionPattern;
741 LogicalResult matchAndRewrite(
742 NonDetOp nondetOp, OpAdaptor, ConversionPatternRewriter &rewriter
744 if (
auto at = dyn_cast<ArrayType>(nondetOp.getType())) {
745 auto wildcardTy = llvm::cast<ArrayType>(replaceAffineMapArrayDimsWithWildcards(at));
749 if (wildcardTy == at) {
750 rewriter.replaceOp(nondetOp, newArray);
753 rewriter.replaceOp(nondetOp, cast.getResult());
762static LogicalResult step0(ModuleOp modOp) {
763 MLIRContext *ctx = modOp.getContext();
764 RewritePatternSet patterns {ctx};
765 patterns.add<NondetToNewArray>(ctx);
766 ConversionTarget target {*ctx};
768 baseTargetSetup(target);
771 return applyFullConversion(modOp, target, std::move(patterns));
776step1(ModuleOp modOp, SymbolTableCollection &symTables, MemberReplacementMap &memberRepMap) {
777 MLIRContext *ctx = modOp.getContext();
779 RewritePatternSet patterns(ctx);
781 patterns.add<SplitArrayInMemberDefOp>(ctx, symTables, memberRepMap);
783 ConversionTarget target(*ctx);
784 baseTargetSetup(target);
785 target.addDynamicallyLegalOp<
MemberDefOp>(SplitArrayInMemberDefOp::legal);
787 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 1: split array-type members\n";);
788 return applyFullConversion(modOp, target, std::move(patterns));
794step2(ModuleOp modOp, SymbolTableCollection &symTables,
const MemberReplacementMap &memberRepMap) {
795 MLIRContext *ctx = modOp.getContext();
797 RewritePatternSet patterns(ctx);
800 SplitInitFromCreateArrayOp,
803 SplitArrayInFuncDefOp,
804 SplitArrayInReturnOp,
806 ReplaceKnownArrayLengthOp
812 SplitArrayInMemberWriteOp,
813 SplitArrayInMemberReadOp
815 >(ctx, symTables, memberRepMap);
817 ConversionTarget target(*ctx);
818 baseTargetSetup(target);
819 target.addDynamicallyLegalOp<
CreateArrayOp>(SplitInitFromCreateArrayOp::legal);
820 target.addDynamicallyLegalOp<
InsertArrayOp>(SplitInsertArrayOp::legal);
821 target.addDynamicallyLegalOp<
ExtractArrayOp>(SplitExtractArrayOp::legal);
822 target.addDynamicallyLegalOp<
FuncDefOp>(SplitArrayInFuncDefOp::legal);
823 target.addDynamicallyLegalOp<
ReturnOp>(SplitArrayInReturnOp::legal);
824 target.addDynamicallyLegalOp<
CallOp>(SplitArrayInCallOp::legal);
825 target.addDynamicallyLegalOp<
ArrayLengthOp>(ReplaceKnownArrayLengthOp::legal);
826 target.addDynamicallyLegalOp<
MemberWriteOp>(SplitArrayInMemberWriteOp::legal);
827 target.addDynamicallyLegalOp<
MemberReadOp>(SplitArrayInMemberReadOp::legal);
829 LLVM_DEBUG(llvm::dbgs() <<
"Begin step 2: update/split other array ops\n";);
830 return applyFullConversion(modOp, target, std::move(patterns));
839static bool mayWriteToIndex(
WriteArrayOp writeOp, ArrayAttr index) {
840 ArrayAttr writeIndex = getIndexAsAttr(writeOp);
841 return !writeIndex || writeIndex == index;
845static bool hasEarlierWriteInBlock(
ReadArrayOp readOp, ArrayAttr readIndex) {
847 for (Operation &op : *readOp->getBlock()) {
848 if (&op == readOp.getOperation()) {
852 if (
auto writeOp = dyn_cast<WriteArrayOp>(&op)) {
853 if (writeOp.getArrRef() == arrRef && mayWriteToIndex(writeOp, readIndex)) {
861 if (writeOp.getArrRef() == arrRef && mayWriteToIndex(writeOp, readIndex)) {
862 return WalkResult::interrupt();
864 return WalkResult::advance();
865 }).wasInterrupted()) {
873static std::optional<WriteArrayOp> findPrecedingWriteForIfRead(
ReadArrayOp readOp) {
874 ArrayAttr readIndex = getIndexAsAttr(readOp);
880 auto ifOp = readOp->getParentOfType<scf::IfOp>();
881 if (!ifOp || readOp->getBlock()->getParentOp() != ifOp.getOperation()) {
884 if (hasEarlierWriteInBlock(readOp, readIndex)) {
888 Block *ifBlock = ifOp->getBlock();
895 for (Operation &op : *ifBlock) {
896 if (&op == ifOp.getOperation()) {
900 if (
auto writeOp = dyn_cast<WriteArrayOp>(&op)) {
901 if (writeOp.getArrRef() != arrRef) {
905 if (mayWriteToIndex(writeOp, readIndex)) {
906 ArrayAttr writeIndex = getIndexAsAttr(writeOp);
907 replacement = writeIndex == readIndex ? writeOp :
WriteArrayOp();
913 if (op.walk([arrRef, readIndex, &replacement](
WriteArrayOp writeOp) {
914 if (writeOp.getArrRef() == arrRef && mayWriteToIndex(writeOp, readIndex)) {
915 replacement = WriteArrayOp();
916 return WalkResult::interrupt();
918 return WalkResult::advance();
919 }).wasInterrupted()) {
924 return replacement ? std::make_optional(replacement) : std::nullopt;
929static void step3(ModuleOp modOp) {
930 SmallVector<std::pair<ReadArrayOp, Value>> replacements;
932 if (std::optional<WriteArrayOp> writeOp = findPrecedingWriteForIfRead(readOp)) {
933 replacements.emplace_back(readOp, writeOp->getRvalue());
937 for (
auto [readOp, value] : replacements) {
938 readOp.
getResult().replaceAllUsesWith(value);
946static void foldConstantGlobalArrayReads(ModuleOp module) {
947 SymbolTableCollection tables;
950 auto arrayType = llvm::dyn_cast<ArrayType>(globalRead.getType());
951 if (!arrayType || !arrayType.hasStaticShape()) {
955 llvm::cast<global::GlobalRefOpInterface>(globalRead.getOperation()).getGlobalDefOp(tables);
960 llvm::dyn_cast_if_present<ArrayAttr>(
definition->get().getInitialValueAttr());
965 SmallVector<std::pair<ReadArrayOp, Attribute>> reads;
967 for (Operation *user : globalRead.getResult().getUsers()) {
968 auto read = llvm::dyn_cast<ReadArrayOp>(user);
969 if (!read || read.getArrRef() != globalRead.getResult()) {
972 ArrayAttr indices = getIndexAsAttr(read);
976 std::optional<int64_t> index = indexGen.linearize(indices.getValue());
980 Attribute element = initializer[*index];
981 auto typed = llvm::dyn_cast<TypedAttr>(element);
982 if (!typed || typed.getType() != read.getType() ||
983 !llvm::isa<IntegerAttr, felt::FeltConstAttr, StringAttr>(element)) {
986 reads.emplace_back(read, element);
989 for (
auto [read, element] : reads) {
990 OpBuilder builder(read);
992 if (
auto integer = llvm::dyn_cast<IntegerAttr>(element)) {
993 constant = arith::ConstantOp::create(builder, read.getLoc(), integer);
994 }
else if (
auto felt = llvm::dyn_cast<felt::FeltConstAttr>(element)) {
998 builder, read.getLoc(), read.getType(), llvm::cast<StringAttr>(element)
1001 read.getResult().replaceAllUsesWith(constant);
1014static bool hasStraightLineRegionPath(Block *allocationBlock, Block *accessBlock) {
1015 for (Block *block = accessBlock; block != allocationBlock;) {
1016 Operation *parent = block->getParentOp();
1017 if (!parent || !isa<scf::ExecuteRegionOp>(parent) ||
1018 !llvm::hasSingleElement(*block->getParent())) {
1021 block = parent->getBlock();
1028struct StraightLineStaticAccess {
1041static bool collectStraightLineStaticElements(
1042 CreateArrayOp alloc, DenseMap<Attribute, Block *> &elementBlocks,
1043 SmallVectorImpl<StraightLineStaticAccess> &accesses
1046 if (!type.hasStaticShape() || !alloc.
getElements().empty()) {
1050 DenseSet<Attribute> ineligibleElements;
1052 Operation *owner =
use.getOwner();
1053 if (
use.getOperandNumber() != 0) {
1058 if (
auto read = dyn_cast<ReadArrayOp>(owner)) {
1059 index = mlir::cast<ArrayAccessOpInterface>(owner).indexOperandsToAttributeArray();
1060 if (!index || read.getResult().getType() != type.
getElementType()) {
1063 }
else if (
auto write = dyn_cast<WriteArrayOp>(owner)) {
1064 index = mlir::cast<ArrayAccessOpInterface>(owner).indexOperandsToAttributeArray();
1065 if (!index || write.getRvalue().getType() != type.
getElementType()) {
1072 Block *accessBlock = owner->getBlock();
1073 accesses.push_back({owner, index, accessBlock});
1074 auto [it, inserted] = elementBlocks.try_emplace(index, accessBlock);
1075 if ((!inserted && it->second != accessBlock) ||
1076 !hasStraightLineRegionPath(alloc->getBlock(), accessBlock)) {
1077 ineligibleElements.insert(index);
1080 for (Attribute index : ineligibleElements) {
1081 elementBlocks.erase(index);
1089static void promoteStraightLineStaticArrays(ModuleOp module) {
1090 using ElementKey = std::pair<Value, Attribute>;
1091 DenseMap<Block *, DenseMap<Operation *, ElementKey>> accessesByBlock;
1092 SmallVector<CreateArrayOp> allocations;
1093 size_t eligibleElementCount = 0;
1094 module.walk([&](CreateArrayOp alloc) {
1095 DenseMap<Attribute, Block *> elementBlocks;
1096 SmallVector<StraightLineStaticAccess> accesses;
1097 if (!collectStraightLineStaticElements(alloc, elementBlocks, accesses)) {
1100 allocations.push_back(alloc);
1101 eligibleElementCount += elementBlocks.size();
1102 for (
const StraightLineStaticAccess &access : accesses) {
1103 if (elementBlocks.contains(access.index)) {
1104 accessesByBlock[access.block].try_emplace(access.op, alloc.
getResult(), access.index);
1109 llvm::dbgs() <<
"Straight-line static array promotion: " << eligibleElementCount
1110 <<
" elements eligible\n";
1113 for (
auto &[block, eligibleAccesses] : accessesByBlock) {
1114 DenseMap<ElementKey, Value> latestValues;
1116 for (Operation &op : llvm::make_early_inc_range(*block)) {
1117 auto accessIt = eligibleAccesses.find(&op);
1118 if (accessIt == eligibleAccesses.end()) {
1121 auto [
array, index] = accessIt->second;
1123 if (
auto read = dyn_cast<ReadArrayOp>(&op)) {
1124 Value &latest = latestValues[{
array, index}];
1126 OpBuilder builder(read);
1129 read.getResult().replaceAllUsesWith(latest);
1134 if (
auto write = dyn_cast<WriteArrayOp>(&op)) {
1135 latestValues[{
array, index}] = write.getRvalue();
1148class StraightLineStaticArrayPromotionPassImpl
1150 StraightLineStaticArrayPromotionPassImpl> {
1152 void runOnOperation()
override { promoteStraightLineStaticArrays(getOperation()); }
1157 using Base = ArrayToScalarPassBase<PassImpl>;
1161 static OpPassManager createPostScalarizationPipeline() {
1162 OpPassManager pm(ModuleOp::getOperationName());
1174 RemoveUnusedDiscardableAllocationsPassOptions {
1183 pm.addPass(createCanonicalizerPass());
1187 void getDependentDialects(DialectRegistry ®istry)
const override {
1188 auto nestedPM = createPostScalarizationPipeline();
1189 nestedPM.getDependentDialects(registry);
1192 void runOnOperation()
override {
1193 ModuleOp module = getOperation();
1195 if (failed(step0(module))) {
1196 return signalPassFailure();
1199 llvm::dbgs() <<
"After step 0:\n";
1208 SymbolTableCollection symTables;
1209 MemberReplacementMap memberRepMap;
1210 if (failed(step1(module, symTables, memberRepMap))) {
1211 return signalPassFailure();
1214 llvm::dbgs() <<
"After step 1:\n";
1218 if (failed(step2(module, symTables, memberRepMap))) {
1219 return signalPassFailure();
1222 llvm::dbgs() <<
"After step 2:\n";
1229 llvm::dbgs() <<
"After step 3:\n";
1233 foldConstantGlobalArrayReads(module);
1235 OpPassManager nestedPM = createPostScalarizationPipeline();
1236 if (failed(runPipeline(nestedPM, module))) {
1237 signalPassFailure();
1241 llvm::dbgs() <<
"After SROA+Mem2Reg pipeline:\n";
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 and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this definition
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)
Common implementation for handling MemberWriteOp and MemberReadOp while destructuring an aggregate ty...
static void genWrite(::mlir::OpBuilder &bldr, ::mlir::Location loc, ::mlir::Value arrayRef, ::mlir::ValueRange indices, ::mlir::Value value)
Create an array.write or array.insert for one concrete element or subarray.
::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.
::mlir::ArrayAttr indexOperandsToAttributeArray()
Returns the multi-dimensional indices of the array access as an Attribute array or a null pointer if ...
static ArrayIndexGen from(ArrayType)
Construct new ArrayIndexGen. Will assert if hasStaticShape() is false.
std::optional< llvm::SmallVector< mlir::Value > > delinearize(int64_t, mlir::Location, mlir::OpBuilder &) const
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of the referenced base array.
::mlir::TypedValue<::mlir::IndexType > getDim()
std::optional<::llvm::SmallVector<::mlir::ArrayAttr > > getSubelementIndices() const
Return a list of all valid indices for this ArrayType.
::mlir::Type getElementType() const
::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()
static constexpr ::llvm::StringLiteral getOperationName()
::mlir::MutableOperandRange getElementsMutable()
::mlir::Operation::operand_range getElements()
::mlir::TypedValue<::llzk::array::ArrayType > getRvalue()
::mlir::TypedValue<::mlir::Type > getResult()
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
static WriteArrayOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Value arr_ref, ::mlir::ValueRange indices, ::mlir::Value rvalue)
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::StringAttr getSymNameAttr()
static MemberDefOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::StringAttr sym_name, ::mlir::TypeAttr type, bool isSignal=false, bool isColumn=false)
static MemberReadOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type resultType, ::mlir::Value component, ::mlir::StringAttr member)
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.
LLZK constraint emission operations.
static FeltConstantOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::llzk::felt::FeltConstAttr value)
LLZK felt (Field ELemenT) type and operations.
::llvm::SmallVector< RangeT > getMapOperands()
::mlir::MutableOperandRange getArgOperandsMutable()
::mlir::Operation::operand_range getArgOperands()
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
::llvm::ArrayRef<::mlir::Type > getResultTypes()
Required by FunctionOpInterface.
LLZK function operations.
::mlir::MutableOperandRange getOperandsMutable()
::mlir::Operation::operand_range getOperands()
LLZK support for global values, defined outside of structs.
Dialect for working with plain-old-data structs.
LLZK types and operations to support templated/parameterized structs.
static UnifiableCastOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::Value input)
LLZK support for random access memory during witness generation.
static LitStringOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::mlir::StringAttr value)
LLZK string type and operations.
std::unique_ptr<::mlir::Pass > createStraightLineStaticArrayPromotionPass()
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.
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()
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...
constexpr T checkedCast(U u) noexcept
OpTy preserveDiscardableAttrs(mlir::Operation *src, OpTy dst)
Copy discardable attributes from src to dst.
std::unique_ptr< SpecializedMem2Reg< AllocOpTy > > createSpecializedMem2RegPass()
int64_t fromAPInt(const llvm::APInt &i)
Extract a verified MLIR index value; malformed internal IR is a fatal error.
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.
std::unique_ptr< SpecializedSROA< AllocOpTy > > createSpecializedSROAPass()
llvm::SmallVector< std::optional< llvm::StringRef > > originalNames
llvm::SmallVector< llvm::StringRef > existingNames
llvm::SmallVector< llvm::SmallVector< std::string > > splitNameSuffixes