22#include <mlir/IR/AsmState.h>
38static inline std::strong_ordering comparePointers(
const void *lhs,
const void *rhs) {
39 return std::compare_three_way {}(lhs, rhs);
43compareDynamicAPInt(
const llvm::DynamicAPInt &lhs,
const llvm::DynamicAPInt &rhs) {
45 return std::strong_ordering::less;
48 return std::strong_ordering::greater;
50 return std::strong_ordering::equal;
53std::strong_ordering compareStringRef(llvm::StringRef lhs, llvm::StringRef rhs) {
54 int cmp = lhs.compare(rhs);
56 return std::strong_ordering::less;
59 return std::strong_ordering::greater;
61 return std::strong_ordering::equal;
67LogicalResult printSourceStylePath(raw_ostream &os, llvm::ArrayRef<SourceRefIndex> path) {
68 for (
const auto &idx : path) {
70 os <<
'.' << idx.getMember().getName();
73 if (idx.isPodRecord()) {
74 os <<
'.' << idx.getPodRecordName();
77 if (idx.isIndex() || idx.isIndexRange()) {
78 os <<
'[' << idx <<
']';
87compareSourceRefPaths(llvm::ArrayRef<SourceRefIndex> lhs, llvm::ArrayRef<SourceRefIndex> rhs) {
88 for (
size_t i = 0; i < lhs.size() && i < rhs.size(); i++) {
89 if (
auto cmp = lhs[i] <=> rhs[i];
cmp != std::strong_ordering::equal) {
93 return lhs.size() <=> rhs.size();
101 assert((size >= 0 || ShapedType::isDynamic(size)) &&
"invalid array dimension");
102 return SourceRefIndex(llvm::DynamicAPInt(0), llvm::DynamicAPInt(size));
121 os << low <<
':' << high;
129 return std::strong_ordering::less;
132 return std::strong_ordering::greater;
134 return std::strong_ordering::equal;
145 if (
auto cmp = compareDynamicAPInt(ll, rl);
cmp != std::strong_ordering::equal) {
148 return compareDynamicAPInt(lu, ru);
152 return std::strong_ordering::less;
155 return std::strong_ordering::greater;
158 return std::strong_ordering::less;
161 return std::strong_ordering::greater;
164 return std::strong_ordering::less;
166 return std::strong_ordering::greater;
205SourceRef::SortCategory SourceRef::getSortCategory()
const {
206 if (isBlockArgument()) {
207 return SortCategory::BlockArgument;
209 if (isCreateStructOp()) {
210 return SortCategory::CreateStruct;
213 return SortCategory::NonDet;
216 return SortCategory::RootResult;
218 if (isTemplateConstant()) {
219 return SortCategory::TemplateConstant;
221 if (isConstantIndex()) {
222 return SortCategory::ConstantIndex;
224 if (isConstantFelt()) {
225 return SortCategory::ConstantFelt;
228 llvm::errs() << *
this <<
'\n';
229 llvm_unreachable(
"unhandled SourceRef sort category");
232StringRef SourceRef::getTemplateConstantName()
const {
233 auto constantVal = getConstant();
234 ensure(succeeded(constantVal),
"template constant must be constant");
235 auto constRead = llvm::dyn_cast<ConstReadOp>(constantVal->getDefiningOp());
236 ensure(constRead,
"template constant must be backed by const.read");
237 return constRead.getConstName();
241SourceRef::compareWithinCategory(
const SourceRef &rhs, SortCategory category)
const {
243 case SortCategory::BlockArgument: {
244 if (
auto cmp = *getInputNum() <=> *rhs.getInputNum();
cmp != std::strong_ordering::equal) {
247 if (
auto cmp = comparePointers(getAsOpaquePointer(), rhs.getAsOpaquePointer());
248 cmp != std::strong_ordering::equal) {
251 return compareSourceRefPaths(getPath(), rhs.getPath());
253 case SortCategory::CreateStruct:
254 case SortCategory::NonDet:
255 case SortCategory::RootResult: {
256 if (
auto cmp = comparePointers(getAsOpaquePointer(), rhs.getAsOpaquePointer());
257 cmp != std::strong_ordering::equal) {
260 return compareSourceRefPaths(getPath(), rhs.getPath());
262 case SortCategory::TemplateConstant: {
263 if (
auto cmp = compareStringRef(getTemplateConstantName(), rhs.getTemplateConstantName());
264 cmp != std::strong_ordering::equal) {
267 return comparePointers(getAsOpaquePointer(), rhs.getAsOpaquePointer());
269 case SortCategory::ConstantIndex:
270 return compareDynamicAPInt(*getConstantIndexValue(), *rhs.getConstantIndexValue());
271 case SortCategory::ConstantFelt:
274 comparePointers(getType().getAsOpaquePointer(), rhs.getType().getAsOpaquePointer());
275 cmp != std::strong_ordering::equal) {
278 return compareDynamicAPInt(getConstantIdentityValue(), rhs.getConstantIdentityValue());
281 llvm_unreachable(
"unhandled SourceRef category compare");
291SymbolLookupResult<StructDefOp>
299 return std::move(*sDef);
302std::vector<SourceRef>
304 std::vector<SourceRef> res = {root};
305 for (
const SourceRef &child : root.getAllChildren(tables,
mod)) {
306 auto recursiveChildren = getAllSourceRefs(tables,
mod, child);
307 res.insert(res.end(), recursiveChildren.begin(), recursiveChildren.end());
313 std::vector<SourceRef> res;
316 structDef == fnOp->getParentOfType<
StructDefOp>(),
"function must be within the given struct"
320 ensure(succeeded(modOp),
"could not lookup module from struct " + Twine(structDef.getName()));
322 SymbolTableCollection tables;
323 for (
auto a : fnOp.getArguments()) {
325 res.insert(res.end(), argRes.begin(), argRes.end());
332 auto createOp = dyn_cast_if_present<CreateStructOp>(selfVal.getDefiningOp());
333 ensure(createOp,
"self value should originate from struct.new operation");
334 auto selfRes =
getAllSourceRefs(tables, modOp.value(), SourceRef(createOp));
335 res.insert(res.end(), selfRes.begin(), selfRes.end());
342 std::vector<SourceRef> res;
345 memberDef->getParentOfType<
StructDefOp>() == structDef,
346 "Member " + Twine(memberDef.getName()) +
" is not a member of struct " +
347 Twine(structDef.getName())
350 ensure(succeeded(modOp),
"could not lookup module from struct " + Twine(structDef.getName()));
353 BlockArgument self = constrainFnOp.getArguments().front();
354 SourceRef memberRef = SourceRef(self, {
SourceRefIndex(memberDef)});
356 SymbolTableCollection tables;
361 Type currTy = value.getType();
362 for (
const auto &idx :
getPath()) {
363 if (idx.isMember()) {
364 currTy = idx.getMember().getType();
367 if (idx.isPodRecord()) {
368 auto podTy = dyn_cast<pod::PodType>(currTy);
369 ensure(
static_cast<bool>(podTy),
"SourceRef pod record requires a pod-typed base");
370 auto lookup = podTy.getRecord(idx.getPodRecordName(), [ctx = value.getContext()]() {
371 return mlir::emitError(
372 mlir::UnknownLoc::get(ctx),
"SourceRef references a missing pod record"
375 ensure(succeeded(lookup),
"SourceRef references a missing pod record");
380 auto arrTy = dyn_cast<ArrayType>(currTy);
381 ensure(
static_cast<bool>(arrTy),
"SourceRef array index requires an array-typed base");
382 currTy = arrTy.getSelectionType(1);
393 auto prefixPath = prefix.
getPath();
394 if (value != prefix.value || pathRef.size() < prefixPath.size()) {
397 for (
size_t i = 0; i < prefixPath.size(); i++) {
398 if (pathRef[i] != prefixPath[i]) {
406 auto getSelfStruct = [](
const SourceRef &ref) ->
StructDefOp {
407 if (
auto createOp = dyn_cast_if_present<CreateStructOp>(ref.value.getDefiningOp())) {
408 auto func = createOp->getParentOfType<
FuncDefOp>();
409 if (!func || !func.isStructCompute() || func.getSelfValueFromCompute() != ref.value) {
414 auto blockArg = ref.getBlockArgument();
415 if (failed(blockArg)) {
418 auto func = dyn_cast_if_present<FuncDefOp>(blockArg->getOwner()->getParentOp());
419 return func && func.isStructConstrain() && func.getSelfValueFromConstrain() == *blockArg
423 bool sameRoot = value == rhs.value;
427 sameRoot = lhsStruct && lhsStruct == rhsStruct;
432 return llvm::all_of(llvm::zip(path, rhs.path), [](
const auto &indices) {
433 return std::get<0>(indices).overlaps(std::get<1>(indices));
438 llvm::SmallVector<SourceRefIndex> selections;
440 return index.isIndex() || index.isIndexRange();
443 SourceRef result = *
this;
444 size_t dimension = 0;
449 if (dimension < selections.size() && index.
isIndexRange() && selections[dimension].isIndex() &&
450 index.
overlaps(selections[dimension])) {
451 index = selections[dimension];
464 auto prefixPath = prefix.
getPath();
465 suffix.reserve(pathRef.size() - prefixPath.size());
466 for (
size_t i = prefixPath.size(); i < pathRef.size(); i++) {
467 suffix.push_back(pathRef[i]);
477 if (failed(suffix)) {
481 SourceRef newSignalUsage = other;
484 pathRef.insert(pathRef.end(), suffix->begin(), suffix->end());
487 return newSignalUsage;
493 std::vector<SourceRef> res;
495 for (int64_t i = 0; i < arrayTy.getDimSize(0); i++) {
497 ensure(succeeded(childRef),
"array children require a rooted SourceRef");
498 res.push_back(*childRef);
508 std::vector<SourceRef> res;
516 auto structDefCopy = structDefRes;
518 tables, SymbolRefAttr::get(f.getContext(), f.getSymNameAttr()), std::move(structDefCopy),
521 ensure(succeeded(memberLookup),
"could not get SymbolLookupResult of existing MemberDefOp");
523 ensure(succeeded(childRef),
"struct children require a rooted SourceRef");
526 res.push_back(*childRef);
532 std::vector<SourceRef> res;
535 ensure(succeeded(childRef),
"pod children require a rooted SourceRef");
536 res.push_back(*childRef);
541std::vector<SourceRef>
544 if (
auto structTy = dyn_cast<StructType>(ty)) {
546 }
else if (
auto podTy = dyn_cast<pod::PodType>(ty)) {
548 }
else if (
auto arrayType = dyn_cast<ArrayType>(ty)) {
555static void printCallResultFallback(raw_ostream &os,
function::CallOp callOp, Value value) {
558 Operation *printScope = callOp.getOperation();
559 if (
auto funcOp = callOp->getParentOfType<
FuncDefOp>()) {
560 printScope = funcOp.getOperation();
564 AsmState state(printScope);
565 value.printAsOperand(os, state);
569static bool shouldPrintNamedCallResult(
570 function::CallOp callOp, OpResult callResult, function::FuncDefOp calleeFunc
572 auto resName = calleeFunc.getResNameAttr(callResult.getResultNumber());
577 auto parentFunc = callOp->getParentOfType<FuncDefOp>();
582 bool foundThisCall =
false;
583 bool foundDuplicate =
false;
584 parentFunc.walk([&](function::CallOp otherCall) {
585 if (foundDuplicate) {
586 return WalkResult::interrupt();
589 auto otherFunc = llvm::dyn_cast_if_present<FuncDefOp>(otherCall.resolveCallable());
591 return WalkResult::advance();
593 for (Value otherValue : otherCall->getResults()) {
594 auto otherResult = llvm::cast<OpResult>(otherValue);
595 auto otherResName = otherFunc.getResNameAttr(otherResult.getResultNumber());
596 if (!otherResName || otherResName->getValue() != resName->getValue()) {
599 if (otherResult == callResult) {
600 foundThisCall =
true;
603 foundDuplicate =
true;
604 return WalkResult::interrupt();
606 return WalkResult::advance();
609 return foundThisCall && !foundDuplicate;
614 os <<
"<felt.const: " << getConstantIdentityValue() <<
'>';
618 auto constRead = getDefiningOp<ConstReadOp>();
619 ensure(succeeded(constRead),
"template constant should be backed by a const.read op");
620 auto structDefOp = (*constRead)->getParentOfType<
StructDefOp>();
621 ensure(structDefOp,
"struct template should have a struct parent");
622 os <<
'@' << structDefOp.getName() <<
"<[@" << constRead->getConstName() <<
"]>";
628 auto funcOp = llvm::dyn_cast<FuncDefOp>(blockArg.getOwner()->getParentOp());
631 if (funcOp && funcOp.isStructConstrain() && funcOp.getSelfValueFromConstrain() == blockArg) {
634 std::optional<StringAttr> argName;
636 argName = funcOp.getArgNameAttr(blockArg.getArgNumber());
639 os << argName->getValue();
648 auto callResult = llvm::cast<OpResult>(value);
650 if (succeeded(callee)) {
652 if (calleeFunc && shouldPrintNamedCallResult(callOp, callResult, calleeFunc)) {
653 auto resName = *calleeFunc.
getResNameAttr(callResult.getResultNumber());
654 os << resName.getValue();
656 printCallResultFallback(os, callOp, value);
659 printCallResultFallback(os, callOp, value);
663 OpPrintingFlags flags;
664 value.printAsOperand(os, flags);
667 auto res = printSourceStylePath(os,
getPath());
668 ensure(succeeded(res),
"unhandled path print case");
673 if (
auto feltConst = getDefiningOp<FeltConstantOp>(); succeeded(feltConst)) {
676 return field.
reduce(feltConst->getValue().getReducedValueOrRaw());
681DynamicAPInt SourceRef::getConstantIdentityValue()
const {
682 if (
auto feltConst = getDefiningOp<FeltConstantOp>(); succeeded(feltConst)) {
685 return feltConst->getValue().getReducedValueOrRaw();
687 auto index = getConstantIndexValue();
688 ensure(succeeded(index),
"expected a numeric constant reference");
695 DynamicAPInt lhsVal = getConstantIdentityValue(), rhsVal = rhs.getConstantIdentityValue();
698 return constant == rhs.constant && value == rhs.value && llvm::equal(
getPath(), rhs.
getPath());
703 auto lhsCategory = getSortCategory();
704 auto rhsCategory = rhs.getSortCategory();
705 if (
auto cmp = lhsCategory <=> rhsCategory;
cmp != std::strong_ordering::equal) {
708 return compareWithinCategory(rhs, lhsCategory);
715 return llvm::hash_value(val.getAsOpaquePointer());
719 "unhandled SourceRef hash case"
722 size_t hash = llvm::hash_value(val.getAsOpaquePointer());
723 for (
const auto &f : val.
getPath()) {
724 hash = llvm::hash_combine(hash, f.getHash());
738 insert(rhs.begin(), rhs.end());
744 std::vector<SourceRef> sortedRefs(rhs.begin(), rhs.end());
745 std::sort(sortedRefs.begin(), sortedRefs.end());
746 for (
auto it = sortedRefs.begin(); it != sortedRefs.end();) {
749 if (it != sortedRefs.end()) {
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
Shared utility function implementations for LLZK lowering passes.
This file defines methods symbol lookup across LLZK operations and included files.
Information about the prime finite field used for the interval analysis.
llvm::DynamicAPInt reduce(const llvm::DynamicAPInt &i) const
Returns the canonical representative of i mod p in [0, p).
Defines an index into an LLZK object.
std::strong_ordering operator<=>(const SourceRefIndex &rhs) const
bool isIndexRange() const
static SourceRefIndex forArrayDimension(int64_t size)
Select every nonnegative index in a static or dynamic array dimension.
bool hasUnboundedUpperBound() const
Whether this range has no finite upper bound.
llvm::DynamicAPInt getIndex() const
void print(mlir::raw_ostream &os) const
IndexRange getIndexRange() const
bool overlaps(const SourceRefIndex &rhs) const
Return true when these path components select any common storage.
component::MemberDefOp getMember() const
SourceRefIndex(component::MemberDefOp f)
llvm::StringRef getPodRecordName() const
SourceRefSet & join(const SourceRefSet &rhs)
A reference to a "source", which is the base value from which other SSA values are derived.
bool isBlockArgument() const
bool overlaps(const SourceRef &rhs) const
Return true when both references select overlapping storage at the same path depth.
mlir::FailureOr< SourceRef > createChild(const SourceRefIndex &r) const
std::vector< SourceRef > getAllChildren(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod) const
Get all direct children of this SourceRef, assuming this ref is not a scalar.
mlir::FailureOr< std::vector< SourceRefIndex > > getSuffix(const SourceRef &prefix) const
If prefix is a valid prefix of this reference, return the suffix that remains after removing the pref...
mlir::FailureOr< function::CallOp > getCallOp() const
void print(mlir::raw_ostream &os) const
Print this reference using source-style names.
bool isCallResult() const
bool operator==(const SourceRef &rhs) const
bool isConstantFelt() const
llvm::ArrayRef< SourceRefIndex > getPath() const
bool isValidPrefix(const SourceRef &prefix) const
Returns true iff prefix is a valid prefix of this reference.
std::strong_ordering operator<=>(const SourceRef &rhs) const
bool isConstantIndex() const
std::vector< SourceRefIndex > Path
static std::vector< SourceRef > getAllSourceRefs(mlir::SymbolTableCollection &tables, mlir::ModuleOp mod, const SourceRef &root)
Produce all possible SourceRefs that are present starting from the given root.
mlir::FailureOr< llvm::DynamicAPInt > getConstantValue() const
Return a known numeric value, failing for fieldless felt constants.
mlir::FailureOr< unsigned > getInputNum() const
SourceRef narrowRanges(const SourceRef &rhs) const
Return a copy with ranged array indices narrowed by concrete indices from rhs.
mlir::FailureOr< NonDetOp > getNonDetOp() const
mlir::FailureOr< mlir::BlockArgument > getBlockArgument() const
mlir::FailureOr< llvm::DynamicAPInt > getConstantIndexValue() const
mlir::FailureOr< SourceRef > translate(const SourceRef &prefix, const SourceRef &other) const
Create a new reference with prefix replaced with other iff prefix is a valid prefix for this referenc...
bool isTemplateConstant() const
Return whether this reference originates from a template constant read.
bool isConstantInt() const
bool isCreateStructOp() const
mlir::Type getType() const
static constexpr ::llvm::StringLiteral getOperationName()
::llzk::function::FuncDefOp getConstrainFuncOp()
Gets the FuncDefOp that defines the constrain function in this structure, if present,...
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
::mlir::SymbolRefAttr getCallee()
::mlir::Value getSelfValueFromCompute()
Return the "self" value (i.e.
::std::optional<::mlir::StringAttr > getResNameAttr(unsigned index)
Return the function.res_name attribute for the result at the given index.
bool isStructCompute()
Return true iff the function is within a StructDefOp and named FUNC_NAME_COMPUTE.
::llvm::ArrayRef<::llzk::pod::RecordAttr > getRecords() const
ExpressionValue mod(const llvm::SMTSolverRef &solver, const ExpressionValue &lhs, const ExpressionValue &rhs)
hash_code hashDynamicAPInt(const DynamicAPInt &value)
void ensure(bool condition, const llvm::Twine &errMsg)
FailureOr< ModuleOp > getRootModule(Operation *from)
ExpressionValue cmp(const llvm::SMTSolverRef &solver, CmpOp op, const ExpressionValue &lhs, const ExpressionValue &rhs)
Interval operator<<(const Interval &lhs, const Interval &rhs)
mlir::FailureOr< SymbolLookupResult< T > > resolveCallable(mlir::SymbolTableCollection &symbolTable, mlir::CallOpInterface call)
Based on mlir::CallOpInterface::resolveCallable, but using LLZK lookup helpers.
std::vector< SourceRef > getAllChildren(SymbolTableCollection &, ModuleOp, ArrayType arrayTy, const SourceRef &root)
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbolIn(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, Within &&lookupWithin, mlir::Operation *origin, bool reportMissing=true)
SymbolLookupResult< StructDefOp > getStructDef(SymbolTableCollection &tables, ModuleOp mod, StructType ty)
Lookup a StructDefOp from a given StructType.
Order named operations by source location, using the symbol name to break ties or when source locatio...
size_t operator()(const SourceRefIndex &c) const
size_t operator()(const SourceRef &val) const