23#include <mlir/IR/BuiltinOps.h>
24#include <mlir/IR/Dominance.h>
26#include <llvm/ADT/DenseMap.h>
27#include <llvm/ADT/DenseMapInfo.h>
28#include <llvm/ADT/DenseSet.h>
29#include <llvm/ADT/STLExtras.h>
30#include <llvm/ADT/ScopeExit.h>
31#include <llvm/ADT/SmallVector.h>
32#include <llvm/ADT/StringMap.h>
33#include <llvm/Support/Debug.h>
41#define GEN_PASS_DEF_POLYLOWERINGPASS
53#define DEBUG_TYPE "llzk-poly-lowering-pass"
54#define AUXILIARY_MEMBER_PREFIX "__llzk_poly_lowering_pass_aux_member_"
59 std::string auxMemberName;
65struct MutableContainmentElement {
70enum class AuxAssignmentVisitState : uint8_t {
77 using Base = PolyLoweringPassBase<PassImpl>;
80 unsigned auxCounter = 0;
82 void collectStructDefs(ModuleOp modOp, SmallVectorImpl<StructDefOp> &structDefs) {
84 structDefs.push_back(structDef);
85 return WalkResult::skip();
90 void addAuxDependency(
91 unsigned dep,
unsigned owner, DenseSet<unsigned> &seenDeps, SmallVectorImpl<unsigned> &deps
96 if (seenDeps.insert(dep).second) {
102 void collectAuxDependencies(
103 Value val,
unsigned owner,
const DenseMap<Value, unsigned> &auxValueToIndex,
104 const llvm::StringMap<unsigned> &auxNameToIndex, DenseSet<Value> &visitedValues,
105 DenseSet<unsigned> &seenDeps, SmallVectorImpl<unsigned> &deps
109 if (!val || !visitedValues.insert(val).second) {
113 if (
auto it = auxValueToIndex.find(val); it != auxValueToIndex.end()) {
114 addAuxDependency(it->second, owner, seenDeps, deps);
118 auto it = auxNameToIndex.find(readOp.getMemberName());
119 if (it != auxNameToIndex.end()) {
120 addAuxDependency(it->second, owner, seenDeps, deps);
124 if (Operation *defOp = val.getDefiningOp()) {
125 for (Value operand : defOp->getOperands()) {
126 collectAuxDependencies(
127 operand, owner, auxValueToIndex, auxNameToIndex, visitedValues, seenDeps, deps
134 LogicalResult visitAuxAssignment(
135 unsigned idx, ArrayRef<SmallVector<unsigned>> deps,
136 SmallVectorImpl<AuxAssignmentVisitState> &visitState, SmallVectorImpl<unsigned> &ordered,
137 ArrayRef<AuxAssignment> auxAssignments
139 if (visitState[idx] == AuxAssignmentVisitState::Done) {
142 if (visitState[idx] == AuxAssignmentVisitState::Visiting) {
143 return emitError(auxAssignments[idx].computedValue.getLoc())
144 <<
"poly lowering generated cyclic auxiliary dependency involving @"
145 << auxAssignments[idx].auxMemberName;
148 visitState[idx] = AuxAssignmentVisitState::Visiting;
150 for (
unsigned dep : deps[idx]) {
151 if (failed(visitAuxAssignment(dep, deps, visitState, ordered, auxAssignments))) {
155 visitState[idx] = AuxAssignmentVisitState::Done;
156 ordered.push_back(idx);
161 LogicalResult orderAuxAssignments(
162 ArrayRef<AuxAssignment> auxAssignments, SmallVectorImpl<unsigned> &ordered
164 DenseMap<Value, unsigned> auxValueToIndex;
165 llvm::StringMap<unsigned> auxNameToIndex;
166 auxValueToIndex.reserve(auxAssignments.size());
167 for (
auto [idx, assign] : llvm::enumerate(auxAssignments)) {
168 if (assign.auxValue) {
169 auxValueToIndex[assign.auxValue] = idx;
171 auxNameToIndex[assign.auxMemberName] = idx;
174 SmallVector<SmallVector<unsigned>> deps(auxAssignments.size());
175 for (
auto [idx, assign] : llvm::enumerate(auxAssignments)) {
176 DenseSet<Value> visitedValues;
177 DenseSet<unsigned> seenDeps;
178 collectAuxDependencies(
179 assign.computedValue, idx, auxValueToIndex, auxNameToIndex, visitedValues, seenDeps,
184 SmallVector<AuxAssignmentVisitState> visitState(
185 auxAssignments.size(), AuxAssignmentVisitState::Unvisited
187 for (
unsigned idx = 0, e = auxAssignments.size(); idx < e; ++idx) {
188 if (failed(visitAuxAssignment(idx, deps, visitState, ordered, auxAssignments))) {
196 unsigned getDegree(Value val, DenseMap<Value, unsigned> &memo) {
197 if (
auto it = memo.find(val); it != memo.end()) {
201 if (llvm::isa<BlockArgument>(val)) {
206 return memo[val] = 0;
208 if (val.getDefiningOp<
NonDetOp>()) {
209 return memo[val] = 1;
212 return memo[val] = 1;
214 if (
auto addOp = val.getDefiningOp<
AddFeltOp>()) {
215 return memo[val] = std::max(getDegree(addOp.getLhs(), memo), getDegree(addOp.getRhs(), memo));
217 if (
auto subOp = val.getDefiningOp<
SubFeltOp>()) {
218 return memo[val] = std::max(getDegree(subOp.getLhs(), memo), getDegree(subOp.getRhs(), memo));
220 if (
auto mulOp = val.getDefiningOp<
MulFeltOp>()) {
221 return memo[val] = getDegree(mulOp.getLhs(), memo) + getDegree(mulOp.getRhs(), memo);
223 if (
auto divOp = val.getDefiningOp<
DivFeltOp>()) {
224 return memo[val] = getDegree(divOp.getLhs(), memo) + getDegree(divOp.getRhs(), memo);
226 if (
auto negOp = val.getDefiningOp<
NegFeltOp>()) {
227 return memo[val] = getDegree(negOp.getOperand(), memo);
230 llvm_unreachable(
"Unhandled Felt SSA value in degree computation");
233 Value lowerExpression(
235 DominanceInfo &dominanceInfo, DenseMap<Value, unsigned> °reeMemo,
236 DenseMap<Value, Value> &rewrites, SmallVector<AuxAssignment> &auxAssignments
238 auto rewriteIt = rewrites.find(val);
239 if (rewriteIt != rewrites.end() && dominanceInfo.properlyDominates(rewriteIt->second, useOp)) {
240 return rewriteIt->second;
243 auto cacheIdentityRewriteIfAbsent = [&rewrites, &val]() {
245 if (!rewrites.contains(val)) {
250 unsigned degree = getDegree(val, degreeMemo);
251 if (degree <= maxDegree) {
255 cacheIdentityRewriteIfAbsent();
260 auto lowerBinaryRoot = [&](
auto op) -> Value {
261 Value lhs = lowerExpression(
262 op.getLhs(), structDef, constrainFunc, op.getOperation(), dominanceInfo, degreeMemo,
263 rewrites, auxAssignments
265 Value rhs = lowerExpression(
266 op.getRhs(), structDef, constrainFunc, op.getOperation(), dominanceInfo, degreeMemo,
267 rewrites, auxAssignments
270 if (lhs != op.getLhs()) {
271 op.getLhsMutable().set(lhs);
273 if (rhs != op.getRhs()) {
274 op.getRhsMutable().set(rhs);
276 degreeMemo[val] = std::max(getDegree(lhs, degreeMemo), getDegree(rhs, degreeMemo));
277 cacheIdentityRewriteIfAbsent();
281 if (
auto addOp = val.getDefiningOp<
AddFeltOp>()) {
282 return lowerBinaryRoot(addOp);
285 if (
auto subOp = val.getDefiningOp<
SubFeltOp>()) {
286 return lowerBinaryRoot(subOp);
289 if (
auto negOp = val.getDefiningOp<
NegFeltOp>()) {
290 Value operand = lowerExpression(
291 negOp.getOperand(), structDef, constrainFunc, negOp.getOperation(), dominanceInfo,
292 degreeMemo, rewrites, auxAssignments
295 if (operand != negOp.getOperand()) {
296 negOp.getOperandMutable().set(operand);
298 degreeMemo[val] = getDegree(operand, degreeMemo);
299 cacheIdentityRewriteIfAbsent();
303 if (
auto mulOp = val.getDefiningOp<
MulFeltOp>()) {
305 Value lhs = lowerExpression(
306 mulOp.getLhs(), structDef, constrainFunc, mulOp.getOperation(), dominanceInfo, degreeMemo,
307 rewrites, auxAssignments
309 Value rhs = lowerExpression(
310 mulOp.getRhs(), structDef, constrainFunc, mulOp.getOperation(), dominanceInfo, degreeMemo,
311 rewrites, auxAssignments
314 unsigned lhsDeg = getDegree(lhs, degreeMemo);
315 unsigned rhsDeg = getDegree(rhs, degreeMemo);
317 OpBuilder builder(mulOp.getOperation()->getBlock(), ++Block::iterator(mulOp));
319 bool eraseMul = lhsDeg + rhsDeg > maxDegree;
321 if (lhs == rhs && eraseMul) {
326 lhs.getLoc(), lhs.getType(), selfVal, auxMember.getNameAttr()
328 auxAssignments.push_back({auxName, lhs, auxVal});
329 Location loc = builder.getFusedLoc({auxVal.getLoc(), lhs.getLoc()});
333 degreeMemo[auxVal] = 1;
334 rewrites[lhs] = auxVal;
335 rewrites[rhs] = auxVal;
346 while (lhsDeg + rhsDeg > maxDegree) {
347 Value &toFactor = (lhsDeg >= rhsDeg) ? lhs : rhs;
355 toFactor.getLoc(), toFactor.getType(), selfVal, auxMember.getNameAttr()
359 Location loc = builder.getFusedLoc({auxVal.getLoc(), toFactor.getLoc()});
360 auto eqOp = builder.create<
EmitEqualityOp>(loc, auxVal, toFactor);
361 auxAssignments.push_back({auxName, toFactor, auxVal});
363 rewrites[toFactor] = auxVal;
364 degreeMemo[auxVal] = 1;
372 lhsDeg = getDegree(lhs, degreeMemo);
373 rhsDeg = getDegree(rhs, degreeMemo);
377 auto mulVal = builder.create<
MulFeltOp>(lhs.getLoc(), lhs.getType(), lhs, rhs);
379 mulOp->replaceAllUsesWith(mulVal);
384 degreeMemo[mulVal] = lhsDeg + rhsDeg;
385 rewrites[val] = mulVal;
391 cacheIdentityRewriteIfAbsent();
395 Value materializeCallArgument(
397 DominanceInfo &dominanceInfo, DenseMap<Value, unsigned> °reeMemo,
398 DenseMap<Value, Value> &rewrites, SmallVector<AuxAssignment> &auxAssignments
400 Value loweredVal = lowerExpression(
401 val, structDef, constrainFunc, callOp.getOperation(), dominanceInfo, degreeMemo, rewrites,
404 DenseMap<Value, unsigned> checkMemo;
405 if (getDegree(loweredVal, checkMemo) <= 1) {
414 OpBuilder builder(callOp);
417 loweredVal.getLoc(), loweredVal.getType(), selfVal, auxMember.getNameAttr()
420 Location loc = builder.getFusedLoc({auxVal.getLoc(), loweredVal.getLoc()});
422 auxAssignments.push_back({auxName, loweredVal, auxVal});
424 degreeMemo[auxVal] = 1;
425 rewrites[loweredVal] = auxVal;
426 rewrites[val] = auxVal;
430 LogicalResult checkEqualityDegrees(
FuncDefOp constrainFunc) {
431 bool failedCheck =
false;
434 DenseMap<Value, unsigned> checkMemo;
435 unsigned lhsDegree = getDegree(eqOp.
getLhs(), checkMemo);
436 unsigned rhsDegree = getDegree(eqOp.
getRhs(), checkMemo);
439 auto diag = eqOp.emitOpError();
440 diag <<
"poly lowering postcondition failed: equality operand degree exceeds max-degree "
441 <<
maxDegree.getValue() <<
" (lhs degree " << lhsDegree <<
", rhs degree " << rhsDegree
448 return failure(failedCheck);
451 LogicalResult checkStructConstrainCallArguments(
FuncDefOp constrainFunc) {
452 bool failedCheck =
false;
454 constrainFunc.walk([&](
CallOp callOp) {
460 if (!llvm::isa<FeltType>(arg.getType())) {
464 DenseMap<Value, unsigned> checkMemo;
465 unsigned argDegree = getDegree(arg, checkMemo);
467 auto diag = callOp.emitOpError();
468 diag <<
"poly lowering postcondition failed: struct constrain call argument degree "
469 "exceeds 1 (argument degree "
477 return failure(failedCheck);
481 bool isFeltArray(Type type)
const {
482 auto arrayType = llvm::dyn_cast<ArrayType>(type);
486 return llvm::isa<FeltType>(arrayType.getElementType());
490 return containOp.emitOpError()
491 <<
"poly lowering cannot resolve containment RHS row write history: " <<
detail;
495 template <
typename IndexRange,
typename PrefixRange>
496 bool indexStartsWith(
const IndexRange &index,
const PrefixRange &prefix)
const {
497 auto indexIt = index.begin();
498 for (Attribute attr : prefix) {
499 if (indexIt == index.end() || *indexIt != attr) {
508 template <
typename LhsRange,
typename RhsRange>
509 bool prefixesCanOverlap(
const LhsRange &lhs,
const RhsRange &rhs)
const {
510 auto lhsIt = lhs.begin();
511 auto rhsIt = rhs.begin();
512 while (lhsIt != lhs.end() && rhsIt != rhs.end()) {
513 if (*lhsIt != *rhsIt) {
523 ArrayAttr dropIndexPrefix(MLIRContext *ctx, ArrayAttr index,
size_t prefixSize)
const {
524 SmallVector<Attribute> attrs;
526 for (Attribute attr : index) {
527 if (idx++ >= prefixSize) {
528 attrs.push_back(attr);
531 return ArrayAttr::get(ctx, attrs);
535 template <
typename PrefixRange>
536 ArrayAttr appendIndex(MLIRContext *ctx,
const PrefixRange &prefix, ArrayAttr suffix)
const {
537 SmallVector<Attribute> attrs;
538 for (Attribute attr : prefix) {
539 attrs.push_back(attr);
541 for (Attribute attr : suffix) {
542 attrs.push_back(attr);
544 return ArrayAttr::get(ctx, attrs);
548 ArrayAttr getStaticAccessIndex(Operation *op)
const {
549 return llvm::cast<ArrayAccessOpInterface>(op).indexOperandsToAttributeArray();
554 std::optional<SmallVector<ArrayAttr>>
555 getViewIndices(ArrayType arrayType, ArrayRef<Attribute> viewPrefix)
const {
561 SmallVector<ArrayAttr> viewIndices;
562 MLIRContext *ctx = arrayType.getContext();
563 for (ArrayAttr index : *allIndices) {
564 if (indexStartsWith(index, viewPrefix)) {
565 viewIndices.push_back(dropIndexPrefix(ctx, index, viewPrefix.size()));
574 LogicalResult collectMutableContainmentElements(
575 Value arrayValue, Operation *boundaryOp, ArrayRef<Attribute> viewPrefix,
576 EmitContainmentOp containOp, DenseSet<Value> &activeArrays,
577 SmallVectorImpl<MutableContainmentElement> &elements
579 auto arrayType = llvm::dyn_cast<ArrayType>(arrayValue.getType());
580 if (!arrayType || !llvm::isa<FeltType>(arrayType.
getElementType())) {
584 DenseMap<Attribute, OpOperand *> finalElements;
585 if (failed(collectMutableContainmentElementMap(
586 arrayValue, boundaryOp, viewPrefix, containOp, activeArrays, finalElements
591 std::optional<SmallVector<ArrayAttr>> viewIndices = getViewIndices(arrayType, viewPrefix);
593 if (finalElements.empty()) {
596 return emitAmbiguousContainmentRhs(containOp,
"array shape is not static");
599 for (ArrayAttr relativeIndex : *viewIndices) {
600 auto elementIt = finalElements.find(relativeIndex);
601 if (elementIt != finalElements.end()) {
602 elements.push_back(MutableContainmentElement {relativeIndex, elementIt->second});
611 static std::optional<std::pair<Value, FlatSymbolRefAttr>> resolveStructReadSource(Value v) {
612 auto readOp = llvm::dyn_cast_if_present<MemberReadOp>(v.getDefiningOp());
616 return std::make_pair(readOp.getComponent(), readOp.getMemberNameAttr());
624 static bool mayAliasArraySource(Value a, Value b) {
628 auto srcA = resolveStructReadSource(a);
632 auto srcB = resolveStructReadSource(b);
636 return srcA->first == srcB->first && srcA->second == srcB->second;
642 LogicalResult collectMutableContainmentElementMap(
643 Value arrayValue, Operation *boundaryOp, ArrayRef<Attribute> viewPrefix,
644 EmitContainmentOp containOp, DenseSet<Value> &activeArrays,
645 DenseMap<Attribute, OpOperand *> &finalElements
649 auto arrayType = llvm::dyn_cast<ArrayType>(arrayValue.getType());
650 if (!arrayType || !llvm::isa<FeltType>(arrayType.
getElementType())) {
653 if (!boundaryOp || !boundaryOp->getBlock()) {
654 return emitAmbiguousContainmentRhs(containOp,
"missing observation block");
656 if (!activeArrays.insert(arrayValue).second) {
657 return emitAmbiguousContainmentRhs(containOp,
"cyclic array update");
659 auto cleanup = llvm::make_scope_exit([&]() { activeArrays.erase(arrayValue); });
661 MLIRContext *ctx = arrayType.getContext();
663 if (
auto arrayOp = arrayValue.getDefiningOp<CreateArrayOp>()) {
664 MutableOperandRange elementOperands = arrayOp.getElementsMutable();
665 if (!elementOperands.empty()) {
668 return emitAmbiguousContainmentRhs(containOp,
"array.new shape is not static");
670 assert(allIndices->size() == elementOperands.size() &&
"array.new verifier mismatch");
672 auto indexIt = allIndices->begin();
673 for (OpOperand &elementOperand : elementOperands) {
674 ArrayAttr index = *indexIt++;
675 if (indexStartsWith(index, viewPrefix)) {
676 finalElements[dropIndexPrefix(ctx, index, viewPrefix.size())] = &elementOperand;
682 if (
auto extractOp = arrayValue.getDefiningOp<ExtractArrayOp>()) {
683 ArrayAttr extractIndex = getStaticAccessIndex(extractOp.getOperation());
685 return emitAmbiguousContainmentRhs(containOp,
"array.extract index is not static");
688 SmallVector<Attribute> sourcePrefix;
689 for (Attribute attr : extractIndex) {
690 sourcePrefix.push_back(attr);
692 for (Attribute attr : viewPrefix) {
693 sourcePrefix.push_back(attr);
696 if (failed(collectMutableContainmentElementMap(
697 extractOp.getArrRef(), extractOp.getOperation(), sourcePrefix, containOp,
698 activeArrays, finalElements
704 for (Operation &op : *boundaryOp->getBlock()) {
705 if (&op == boundaryOp) {
709 if (
auto writeOp = llvm::dyn_cast<WriteArrayOp>(&op)) {
710 if (!mayAliasArraySource(writeOp.getArrRef(), arrayValue)) {
714 ArrayAttr writeIndex = getStaticAccessIndex(writeOp.getOperation());
716 return emitAmbiguousContainmentRhs(containOp,
"array.write index is not static");
718 if (indexStartsWith(writeIndex, viewPrefix)) {
719 finalElements[dropIndexPrefix(ctx, writeIndex, viewPrefix.size())] =
720 &writeOp.getRvalueMutable();
725 if (
auto insertOp = llvm::dyn_cast<InsertArrayOp>(&op)) {
726 if (!mayAliasArraySource(insertOp.getArrRef(), arrayValue)) {
730 ArrayAttr insertIndex = getStaticAccessIndex(insertOp.getOperation());
732 return emitAmbiguousContainmentRhs(containOp,
"array.insert index is not static");
734 if (!prefixesCanOverlap(insertIndex, viewPrefix)) {
738 auto rvalueType = llvm::dyn_cast<ArrayType>(insertOp.getRvalue().getType());
739 if (!rvalueType || !llvm::isa<FeltType>(rvalueType.getElementType())) {
743 std::optional<SmallVector<ArrayAttr>> rvalueIndices = rvalueType.getSubelementIndices();
744 if (!rvalueIndices) {
745 return emitAmbiguousContainmentRhs(containOp,
"array.insert rvalue shape is not static");
748 SmallVector<MutableContainmentElement> insertedElements;
749 if (failed(collectMutableContainmentElements(
750 insertOp.getRvalue(), insertOp.getOperation(), ArrayRef<Attribute> {}, containOp,
751 activeArrays, insertedElements
756 DenseMap<Attribute, OpOperand *> insertedElementMap;
757 for (MutableContainmentElement element : insertedElements) {
758 insertedElementMap[element.index] = element.operand;
761 for (ArrayAttr rvalueIndex : *rvalueIndices) {
762 ArrayAttr targetIndex = appendIndex(ctx, insertIndex, rvalueIndex);
763 if (!indexStartsWith(targetIndex, viewPrefix)) {
767 ArrayAttr relativeIndex = dropIndexPrefix(ctx, targetIndex, viewPrefix.size());
768 auto elementIt = insertedElementMap.find(rvalueIndex);
769 if (elementIt == insertedElementMap.end()) {
770 finalElements.erase(relativeIndex);
773 finalElements[relativeIndex] = elementIt->second;
783 LogicalResult lowerContainmentRhsFeltOperand(
784 OpOperand &operand, StructDefOp structDef, FuncDefOp constrainFunc,
785 DominanceInfo &dominanceInfo, DenseMap<Value, unsigned> °reeMemo,
786 DenseMap<Value, Value> &rewrites, SmallVector<AuxAssignment> &auxAssignments
788 Value value = operand.get();
789 if (!llvm::isa<FeltType>(value.getType())) {
793 unsigned degree = getDegree(value, degreeMemo);
794 if (degree > maxDegree) {
795 operand.set(lowerExpression(
796 value, structDef, constrainFunc, operand.getOwner(), dominanceInfo, degreeMemo, rewrites,
805 LogicalResult lowerContainmentRhsValue(
806 OpOperand &operand, StructDefOp structDef, FuncDefOp constrainFunc,
807 DominanceInfo &dominanceInfo, DenseMap<Value, unsigned> °reeMemo,
808 DenseMap<Value, Value> &rewrites, SmallVector<AuxAssignment> &auxAssignments,
809 EmitContainmentOp containOp
811 Value value = operand.get();
812 if (llvm::isa<FeltType>(value.getType())) {
813 return lowerContainmentRhsFeltOperand(
814 operand, structDef, constrainFunc, dominanceInfo, degreeMemo, rewrites, auxAssignments
818 if (!isFeltArray(value.getType())) {
822 DenseSet<Value> activeArrays;
823 SmallVector<MutableContainmentElement> elements;
824 if (failed(collectMutableContainmentElements(
825 value, containOp.getOperation(), ArrayRef<Attribute> {}, containOp, activeArrays,
831 for (MutableContainmentElement element : elements) {
832 if (failed(lowerContainmentRhsFeltOperand(
833 *element.operand, structDef, constrainFunc, dominanceInfo, degreeMemo, rewrites,
845 void checkContainmentRhsFeltValue(
846 Value value, EmitContainmentOp containOp, DenseMap<Value, unsigned> &checkMemo,
849 if (!llvm::isa<FeltType>(value.getType())) {
853 unsigned valueDegree = getDegree(value, checkMemo);
854 if (valueDegree <= maxDegree) {
858 auto diag = containOp.emitOpError();
859 diag <<
"poly lowering postcondition failed: containment RHS element degree "
860 "exceeds max-degree "
861 << maxDegree.getValue() <<
" (element degree " << valueDegree <<
')';
868 LogicalResult checkContainmentRhsValue(
869 Value value, EmitContainmentOp containOp, DenseMap<Value, unsigned> &checkMemo,
872 if (llvm::isa<FeltType>(value.getType())) {
873 checkContainmentRhsFeltValue(value, containOp, checkMemo, failedCheck);
877 if (!isFeltArray(value.getType())) {
881 DenseSet<Value> activeArrays;
882 SmallVector<MutableContainmentElement> elements;
883 if (failed(collectMutableContainmentElements(
884 value, containOp.getOperation(), ArrayRef<Attribute> {}, containOp, activeArrays,
890 for (MutableContainmentElement element : elements) {
891 checkContainmentRhsFeltValue(element.operand->get(), containOp, checkMemo, failedCheck);
898 LogicalResult checkContainmentRhsDegrees(FuncDefOp constrainFunc) {
899 bool failedCheck =
false;
900 bool failedCollection =
false;
901 constrainFunc.walk([&](EmitContainmentOp containOp) {
902 DenseMap<Value, unsigned> checkMemo;
903 if (failed(checkContainmentRhsValue(containOp.
getRhs(), containOp, checkMemo, failedCheck))) {
904 failedCollection =
true;
907 return failure(failedCheck || failedCollection);
910 void runOnOperation()
override {
911 ModuleOp moduleOp = getOperation();
915 auto diag = moduleOp.emitError();
916 diag <<
"Invalid max degree: " << maxDegree.getValue() <<
". Must be >= 2.";
922 moduleOp.walk([
this](StructDefOp structDef) {
925 if (!constrainFunc) {
926 auto diag = structDef.emitOpError();
935 auto diag = structDef.emitOpError();
936 diag <<
'"' << structDef.getName() <<
"\" doesn't have a \"@" <<
FUNC_NAME_COMPUTE
958 DenseMap<Value, unsigned> degreeMemo;
959 DenseMap<Value, Value> rewrites;
960 SmallVector<AuxAssignment> auxAssignments;
961 DominanceInfo dominanceInfo(constrainFunc);
964 constrainFunc.walk([&](EmitEqualityOp constraintOp) {
967 unsigned degreeLhs = getDegree(lhsOperand.get(), degreeMemo);
968 unsigned degreeRhs = getDegree(rhsOperand.get(), degreeMemo);
970 if (degreeLhs > maxDegree) {
971 Value loweredExpr = lowerExpression(
972 lhsOperand.get(), structDef, constrainFunc, constraintOp.getOperation(),
973 dominanceInfo, degreeMemo, rewrites, auxAssignments
975 lhsOperand.set(loweredExpr);
977 if (degreeRhs > maxDegree) {
978 Value loweredExpr = lowerExpression(
979 rhsOperand.get(), structDef, constrainFunc, constraintOp.getOperation(),
980 dominanceInfo, degreeMemo, rewrites, auxAssignments
982 rhsOperand.set(loweredExpr);
987 bool failedContainmentLowering =
false;
988 constrainFunc.walk([&](EmitContainmentOp containOp) {
989 if (failed(lowerContainmentRhsValue(
990 containOp.
getRhsMutable(), structDef, constrainFunc, dominanceInfo, degreeMemo,
991 rewrites, auxAssignments, containOp
993 failedContainmentLowering =
true;
996 if (failedContainmentLowering) {
1002 constrainFunc.walk([&](CallOp callOp) {
1004 SmallVector<Value> newOperands = llvm::to_vector(callOp.
getArgOperands());
1005 bool modified =
false;
1007 for (Value &arg : newOperands) {
1008 if (!llvm::isa<FeltType>(arg.getType())) {
1012 DenseMap<Value, unsigned> callMemo;
1013 unsigned deg = getDegree(arg, callMemo);
1016 arg = materializeCallArgument(
1017 arg, structDef, constrainFunc, callOp, dominanceInfo, degreeMemo, rewrites,
1025 OpBuilder builder(callOp);
1026 builder.create<CallOp>(
1027 callOp.getLoc(), callOp.getResultTypes(), callOp.
getCallee(),
1036 if (failed(checkEqualityDegrees(constrainFunc))) {
1037 signalPassFailure();
1041 if (failed(checkContainmentRhsDegrees(constrainFunc))) {
1042 signalPassFailure();
1046 if (failed(checkStructConstrainCallArguments(constrainFunc))) {
1047 signalPassFailure();
1051 DenseMap<Value, Value> rebuildMemo;
1052 Block &computeBlock = computeFunc.
getBody().front();
1053 OpBuilder builder(&computeBlock, computeBlock.getTerminator()->getIterator());
1056 SmallVector<unsigned> orderedAuxAssignments;
1057 orderedAuxAssignments.reserve(auxAssignments.size());
1058 if (failed(orderAuxAssignments(auxAssignments, orderedAuxAssignments))) {
1059 signalPassFailure();
1063 for (
unsigned assignIdx : orderedAuxAssignments) {
1064 const auto &assign = auxAssignments[assignIdx];
1068 signalPassFailure();
1071 builder.create<MemberWriteOp>(
1072 assign.computedValue.getLoc(), selfVal, builder.getStringAttr(assign.auxMemberName),
1075 if (assign.auxValue) {
1078 rebuildMemo[assign.auxValue] = rebuiltExpr;
#define AUXILIARY_MEMBER_PREFIX
std::optional<::llvm::SmallVector<::mlir::ArrayAttr > > getSubelementIndices() const
Return a list of all valid indices for this ArrayType.
::mlir::Type getElementType() const
StructType getType(::std::optional<::mlir::ArrayAttr > constParams={})
Gets the StructType representing this struct.
::llzk::function::FuncDefOp getConstrainFuncOp()
Gets the FuncDefOp that defines the constrain function in this structure, if present,...
::llzk::function::FuncDefOp getComputeFuncOp()
Gets the FuncDefOp that defines the compute function in this structure, if present,...
::mlir::TypedValue<::mlir::Type > getRhs()
::mlir::OpOperand & getRhsMutable()
::mlir::OpOperand & getRhsMutable()
::mlir::TypedValue<::mlir::Type > getLhs()
::mlir::OpOperand & getLhsMutable()
::mlir::TypedValue<::mlir::Type > getRhs()
bool calleeIsStructConstrain()
Return true iff the callee function name is FUNC_NAME_CONSTRAIN within a StructDefOp.
::mlir::SymbolRefAttr getCallee()
::llvm::ArrayRef< int32_t > getNumDimsPerMap()
::mlir::Operation::operand_range getArgOperands()
::mlir::OperandRangeRange getMapOperands()
static ::llvm::SmallVector<::mlir::ValueRange > toVectorOfValueRange(::mlir::OperandRangeRange)
Allocate consecutive storage of the ValueRange instances in the parameter so it can be passed to the ...
::mlir::Value getSelfValueFromCompute()
Return the "self" value (i.e.
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
::mlir::Region & getBody()
::mlir::Pass::Option< unsigned > maxDegree
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Value rebuildExprInCompute(Value val, FuncDefOp computeFunc, OpBuilder &builder, DenseMap< Value, Value > &memo)
void replaceSubsequentUsesWith(Value oldVal, Value newVal, Operation *afterOp)
constexpr char FUNC_NAME_CONSTRAIN[]
MemberDefOp addAuxMember(StructDefOp structDef, StringRef name, Type type)
LogicalResult checkFuncBodyIsStraightLine(FuncDefOp func, StringRef passName)
LogicalResult checkForAuxMemberConflicts(StructDefOp structDef, StringRef prefix)