22#include <mlir/IR/AsmState.h>
38compareDynamicAPInt(
const llvm::DynamicAPInt &lhs,
const llvm::DynamicAPInt &rhs) {
40 return std::strong_ordering::less;
43 return std::strong_ordering::greater;
45 return std::strong_ordering::equal;
48std::strong_ordering compareStringRef(llvm::StringRef lhs, llvm::StringRef rhs) {
49 int cmp = lhs.compare(rhs);
51 return std::strong_ordering::less;
54 return std::strong_ordering::greater;
56 return std::strong_ordering::equal;
62LogicalResult printSourceStylePath(raw_ostream &os, llvm::ArrayRef<SourceRefIndex> path) {
63 for (
const auto &idx : path) {
65 os <<
'.' << idx.getMember().getName();
68 if (idx.isPodRecord()) {
69 os <<
'.' << idx.getPodRecordName();
72 if (idx.isIndex() || idx.isIndexRange()) {
73 os <<
'[' << idx <<
']';
82compareSourceRefPaths(llvm::ArrayRef<SourceRefIndex> lhs, llvm::ArrayRef<SourceRefIndex> rhs) {
83 for (
size_t i = 0; i < lhs.size() && i < rhs.size(); i++) {
84 if (
auto cmp = lhs[i] <=> rhs[i];
cmp != std::strong_ordering::equal) {
88 return lhs.size() <=> rhs.size();
104 if (ShapedType::isDynamic(int64_t(high))) {
107 os << low <<
':' << high;
115 return std::strong_ordering::less;
118 return std::strong_ordering::greater;
120 return std::strong_ordering::equal;
131 if (
auto cmp = compareDynamicAPInt(ll, rl);
cmp != std::strong_ordering::equal) {
134 return compareDynamicAPInt(lu, ru);
138 return std::strong_ordering::less;
141 return std::strong_ordering::greater;
144 return std::strong_ordering::less;
147 return std::strong_ordering::greater;
150 return std::strong_ordering::less;
152 return std::strong_ordering::greater;
162 unsigned requiredBits = idx.getSignificantBits();
163 auto hash = llvm::hash_value(idx.trunc(requiredBits));
167 return llvm::hash_value(std::get<0>(r)) ^ llvm::hash_value(std::get<1>(r));
186 return lhsLow < rhsHigh && rhsLow < lhsHigh;
193SourceRef::SortCategory SourceRef::getSortCategory()
const {
194 if (isBlockArgument()) {
195 return SortCategory::BlockArgument;
197 if (isCreateStructOp()) {
198 return SortCategory::CreateStruct;
201 return SortCategory::NonDet;
204 return SortCategory::RootResult;
206 if (isTemplateConstant()) {
207 return SortCategory::TemplateConstant;
209 if (isConstantIndex()) {
210 return SortCategory::ConstantIndex;
212 if (isConstantFelt()) {
213 return SortCategory::ConstantFelt;
216 llvm::errs() << *
this <<
'\n';
217 llvm_unreachable(
"unhandled SourceRef sort category");
220StringRef SourceRef::getTemplateConstantName()
const {
221 auto constantVal = getConstant();
222 ensure(succeeded(constantVal),
"template constant must be constant");
223 auto constRead = llvm::dyn_cast<ConstReadOp>(constantVal->getDefiningOp());
224 ensure(constRead,
"template constant must be backed by const.read");
225 return constRead.getConstName();
229SourceRef::compareWithinCategory(
const SourceRef &rhs, SortCategory category)
const {
231 case SortCategory::BlockArgument: {
232 if (
auto cmp = *getInputNum() <=> *rhs.getInputNum();
cmp != std::strong_ordering::equal) {
235 if (
auto cmp = getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
236 cmp != std::strong_ordering::equal) {
239 return compareSourceRefPaths(getPath(), rhs.getPath());
241 case SortCategory::CreateStruct:
242 case SortCategory::NonDet:
243 case SortCategory::RootResult: {
244 if (
auto cmp = getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
245 cmp != std::strong_ordering::equal) {
248 return compareSourceRefPaths(getPath(), rhs.getPath());
250 case SortCategory::TemplateConstant: {
251 if (
auto cmp = compareStringRef(getTemplateConstantName(), rhs.getTemplateConstantName());
252 cmp != std::strong_ordering::equal) {
255 return getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
257 case SortCategory::ConstantIndex:
258 return compareDynamicAPInt(*getConstantIndexValue(), *rhs.getConstantIndexValue());
259 case SortCategory::ConstantFelt:
260 return compareDynamicAPInt(*getConstantFeltValue(), *rhs.getConstantFeltValue());
263 llvm_unreachable(
"unhandled SourceRef category compare");
273SymbolLookupResult<StructDefOp>
281 return std::move(*sDef);
284std::vector<SourceRef>
286 std::vector<SourceRef> res = {root};
287 for (
const SourceRef &child : root.getAllChildren(tables,
mod)) {
288 auto recursiveChildren = getAllSourceRefs(tables,
mod, child);
289 res.insert(res.end(), recursiveChildren.begin(), recursiveChildren.end());
295 std::vector<SourceRef> res;
298 structDef == fnOp->getParentOfType<
StructDefOp>(),
"function must be within the given struct"
302 ensure(succeeded(modOp),
"could not lookup module from struct " + Twine(structDef.getName()));
304 SymbolTableCollection tables;
305 for (
auto a : fnOp.getArguments()) {
307 res.insert(res.end(), argRes.begin(), argRes.end());
314 auto createOp = dyn_cast_if_present<CreateStructOp>(selfVal.getDefiningOp());
315 ensure(createOp,
"self value should originate from struct.new operation");
316 auto selfRes =
getAllSourceRefs(tables, modOp.value(), SourceRef(createOp));
317 res.insert(res.end(), selfRes.begin(), selfRes.end());
324 std::vector<SourceRef> res;
327 memberDef->getParentOfType<
StructDefOp>() == structDef,
328 "Member " + Twine(memberDef.getName()) +
" is not a member of struct " +
329 Twine(structDef.getName())
332 ensure(succeeded(modOp),
"could not lookup module from struct " + Twine(structDef.getName()));
335 BlockArgument self = constrainFnOp.getArguments().front();
336 SourceRef memberRef = SourceRef(self, {
SourceRefIndex(memberDef)});
338 SymbolTableCollection tables;
343 Type currTy = value.getType();
344 for (
const auto &idx :
getPath()) {
345 if (idx.isMember()) {
346 currTy = idx.getMember().getType();
349 if (idx.isPodRecord()) {
350 auto podTy = dyn_cast<pod::PodType>(currTy);
351 ensure(
static_cast<bool>(podTy),
"SourceRef pod record requires a pod-typed base");
352 auto lookup = podTy.getRecord(idx.getPodRecordName(), [ctx = value.getContext()]() {
353 return mlir::emitError(
354 mlir::UnknownLoc::get(ctx),
"SourceRef references a missing pod record"
357 ensure(succeeded(lookup),
"SourceRef references a missing pod record");
362 auto arrTy = dyn_cast<ArrayType>(currTy);
363 ensure(
static_cast<bool>(arrTy),
"SourceRef array index requires an array-typed base");
364 currTy = arrTy.getSelectionType(1);
375 auto prefixPath = prefix.
getPath();
376 if (value != prefix.value || pathRef.size() < prefixPath.size()) {
379 for (
size_t i = 0; i < prefixPath.size(); i++) {
380 if (pathRef[i] != prefixPath[i]) {
391 return llvm::all_of(llvm::zip(path, rhs.path), [](
const auto &indices) {
392 return std::get<0>(indices).overlaps(std::get<1>(indices));
402 auto prefixPath = prefix.
getPath();
403 suffix.reserve(pathRef.size() - prefixPath.size());
404 for (
size_t i = prefixPath.size(); i < pathRef.size(); i++) {
405 suffix.push_back(pathRef[i]);
415 if (failed(suffix)) {
419 SourceRef newSignalUsage = other;
422 pathRef.insert(pathRef.end(), suffix->begin(), suffix->end());
425 return newSignalUsage;
431 std::vector<SourceRef> res;
433 for (int64_t i = 0; i < arrayTy.getDimSize(0); i++) {
435 ensure(succeeded(childRef),
"array children require a rooted SourceRef");
436 res.push_back(*childRef);
446 std::vector<SourceRef> res;
454 auto structDefCopy = structDefRes;
456 tables, SymbolRefAttr::get(f.getContext(), f.getSymNameAttr()), std::move(structDefCopy),
459 ensure(succeeded(memberLookup),
"could not get SymbolLookupResult of existing MemberDefOp");
461 ensure(succeeded(childRef),
"struct children require a rooted SourceRef");
464 res.push_back(*childRef);
470 std::vector<SourceRef> res;
473 ensure(succeeded(childRef),
"pod children require a rooted SourceRef");
474 res.push_back(*childRef);
479std::vector<SourceRef>
482 if (
auto structTy = dyn_cast<StructType>(ty)) {
484 }
else if (
auto podTy = dyn_cast<pod::PodType>(ty)) {
486 }
else if (
auto arrayType = dyn_cast<ArrayType>(ty)) {
493static void printCallResultFallback(raw_ostream &os,
function::CallOp callOp, Value value) {
496 Operation *printScope = callOp.getOperation();
497 if (
auto funcOp = callOp->getParentOfType<
FuncDefOp>()) {
498 printScope = funcOp.getOperation();
502 AsmState state(printScope);
503 value.printAsOperand(os, state);
507static bool shouldPrintNamedCallResult(
508 function::CallOp callOp, OpResult callResult, function::FuncDefOp calleeFunc
510 auto resName = calleeFunc.getResNameAttr(callResult.getResultNumber());
515 auto parentFunc = callOp->getParentOfType<FuncDefOp>();
520 bool foundThisCall =
false;
521 bool foundDuplicate =
false;
522 parentFunc.walk([&](function::CallOp otherCall) {
523 if (foundDuplicate) {
524 return WalkResult::interrupt();
527 auto otherFunc = llvm::dyn_cast_if_present<FuncDefOp>(otherCall.resolveCallable());
529 return WalkResult::advance();
531 for (Value otherValue : otherCall->getResults()) {
532 auto otherResult = llvm::cast<OpResult>(otherValue);
533 auto otherResName = otherFunc.getResNameAttr(otherResult.getResultNumber());
534 if (!otherResName || otherResName->getValue() != resName->getValue()) {
537 if (otherResult == callResult) {
538 foundThisCall =
true;
541 foundDuplicate =
true;
542 return WalkResult::interrupt();
544 return WalkResult::advance();
547 return foundThisCall && !foundDuplicate;
556 auto constRead = getDefiningOp<ConstReadOp>();
557 ensure(succeeded(constRead),
"template constant should be backed by a const.read op");
558 auto structDefOp = (*constRead)->getParentOfType<
StructDefOp>();
559 ensure(structDefOp,
"struct template should have a struct parent");
560 os <<
'@' << structDefOp.getName() <<
"<[@" << constRead->getConstName() <<
"]>";
566 auto funcOp = llvm::dyn_cast<FuncDefOp>(blockArg.getOwner()->getParentOp());
567 auto argName = funcOp ? funcOp.getArgNameAttr(blockArg.getArgNumber()) :
nullptr;
569 os << argName->getValue();
577 auto callResult = llvm::cast<OpResult>(value);
579 if (succeeded(callee)) {
580 auto calleeFunc = llvm::dyn_cast_if_present<FuncDefOp>((*callee).get());
581 if (shouldPrintNamedCallResult(callOp, callResult, calleeFunc)) {
582 auto resName = *calleeFunc.getResNameAttr(callResult.getResultNumber());
583 os << resName.getValue();
585 printCallResultFallback(os, callOp, value);
588 printCallResultFallback(os, callOp, value);
592 OpPrintingFlags flags;
593 value.printAsOperand(os, flags);
596 auto res = printSourceStylePath(os,
getPath());
597 ensure(succeeded(res),
"unhandled path print case");
607 return constant == rhs.constant && value == rhs.value && llvm::equal(
getPath(), rhs.
getPath());
612 auto lhsCategory = getSortCategory();
613 auto rhsCategory = rhs.getSortCategory();
614 if (
auto cmp = lhsCategory <=> rhsCategory;
cmp != std::strong_ordering::equal) {
617 return compareWithinCategory(rhs, lhsCategory);
624 return llvm::hash_value(val.getAsOpaquePointer());
628 "unhandled SourceRef hash case"
631 size_t hash = llvm::hash_value(val.getAsOpaquePointer());
632 for (
const auto &f : val.
getPath()) {
633 hash = llvm::hash_combine(hash, f.getHash());
647 insert(rhs.begin(), rhs.end());
653 std::vector<SourceRef> sortedRefs(rhs.begin(), rhs.end());
654 std::sort(sortedRefs.begin(), sortedRefs.end());
655 for (
auto it = sortedRefs.begin(); it != sortedRefs.end();) {
658 if (it != sortedRefs.end()) {
This file implements helper methods for constructing DynamicAPInts.
This file defines methods symbol lookup across LLZK operations and included files.
std::strong_ordering operator<=>(const SourceRefIndex &rhs) const
bool isIndexRange() const
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
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
mlir::FailureOr< llvm::DynamicAPInt > getConstantFeltValue() 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
mlir::FailureOr< unsigned > getInputNum() const
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
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.
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)
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)
APSInt toAPSInt(const DynamicAPInt &i)
SymbolLookupResult< StructDefOp > getStructDef(SymbolTableCollection &tables, ModuleOp mod, StructType ty)
Lookup a StructDefOp from a given StructType.
size_t operator()(const SourceRefIndex &c) const
size_t operator()(const SourceRef &val) const