LLZK 2.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
SourceRef.cpp
Go to the documentation of this file.
1//===-- SourceRef.cpp - SourceRef implementation ----------------*- C++ -*-===//
2//
3// Part of the LLZK Project, under the Apache License v2.0.
4// See LICENSE.txt for license information.
5// Copyright 2025 Veridise Inc.
6// SPDX-License-Identifier: Apache-2.0
7//
8//===----------------------------------------------------------------------===//
9
11
16#include "llzk/Util/Compare.h"
17#include "llzk/Util/Debug.h"
21
22#include <mlir/IR/AsmState.h>
23
24using namespace mlir;
25
26namespace llzk {
27
28using namespace array;
29using namespace component;
30using namespace felt;
31using namespace function;
32using namespace polymorphic;
33using namespace string;
34
35namespace {
36
37std::strong_ordering
38compareDynamicAPInt(const llvm::DynamicAPInt &lhs, const llvm::DynamicAPInt &rhs) {
39 if (lhs < rhs) {
40 return std::strong_ordering::less;
41 }
42 if (rhs < lhs) {
43 return std::strong_ordering::greater;
44 }
45 return std::strong_ordering::equal;
46}
47
48std::strong_ordering compareStringRef(llvm::StringRef lhs, llvm::StringRef rhs) {
49 int cmp = lhs.compare(rhs);
50 if (cmp < 0) {
51 return std::strong_ordering::less;
52 }
53 if (cmp > 0) {
54 return std::strong_ordering::greater;
55 }
56 return std::strong_ordering::equal;
57}
58
59std::strong_ordering
60compareSourceRefPaths(llvm::ArrayRef<SourceRefIndex> lhs, llvm::ArrayRef<SourceRefIndex> rhs) {
61 for (size_t i = 0; i < lhs.size() && i < rhs.size(); i++) {
62 if (auto cmp = lhs[i] <=> rhs[i]; cmp != std::strong_ordering::equal) {
63 return cmp;
64 }
65 }
66 return lhs.size() <=> rhs.size();
67}
68
69} // namespace
70
71/* SourceRefIndex */
72
73void SourceRefIndex::print(raw_ostream &os) const {
74 if (isMember()) {
75 os << '@' << getMember().getName();
76 } else if (isIndex()) {
77 os << getIndex();
78 } else {
79 auto [low, high] = getIndexRange();
80 if (ShapedType::isDynamic(int64_t(high))) {
81 os << "<dynamic>";
82 } else {
83 os << low << ':' << high;
84 }
85 }
86}
87
88std::strong_ordering SourceRefIndex::operator<=>(const SourceRefIndex &rhs) const {
89 if (isMember() && rhs.isMember()) {
91 return std::strong_ordering::less;
92 }
94 return std::strong_ordering::greater;
95 }
96 return std::strong_ordering::equal;
97 }
98 if (isIndex() && rhs.isIndex()) {
99 return compareDynamicAPInt(getIndex(), rhs.getIndex());
100 }
101 if (isIndexRange() && rhs.isIndexRange()) {
102 auto [ll, lu] = getIndexRange();
103 auto [rl, ru] = rhs.getIndexRange();
104 if (auto cmp = compareDynamicAPInt(ll, rl); cmp != std::strong_ordering::equal) {
105 return cmp;
106 }
107 return compareDynamicAPInt(lu, ru);
108 }
109
110 if (isMember()) {
111 return std::strong_ordering::less;
112 }
113 if (rhs.isMember()) {
114 return std::strong_ordering::greater;
115 }
116 if (isIndex()) {
117 return std::strong_ordering::less;
118 }
119 return std::strong_ordering::greater;
120}
121
123 if (c.isIndex()) {
124 // We don't hash the index directly, because the built-in LLVM hash includes
125 // the bitwidth of the APInt in the hash, which is undesirable for this application.
126 // i.e., We want a N-bit version of x to hash to the same value as an M-bit version of X,
127 // because our equality checks would consider them equal regardless of bitwidth.
128 APSInt idx = toAPSInt(c.getIndex());
129 unsigned requiredBits = idx.getSignificantBits();
130 auto hash = llvm::hash_value(idx.trunc(requiredBits));
131 return hash;
132 } else if (c.isIndexRange()) {
133 auto r = c.getIndexRange();
134 return llvm::hash_value(std::get<0>(r)) ^ llvm::hash_value(std::get<1>(r));
135 } else {
137 }
138}
139
140/* SourceRef */
141
142SourceRef::SortCategory SourceRef::getSortCategory() const {
143 if (isBlockArgument()) {
144 return SortCategory::BlockArgument;
145 }
146 if (isCreateStructOp()) {
147 return SortCategory::CreateStruct;
148 }
149 if (isNonDetOp()) {
150 return SortCategory::NonDet;
151 }
152 if (isRooted()) {
153 return SortCategory::RootResult;
154 }
155 if (isTemplateConstant()) {
156 return SortCategory::TemplateConstant;
157 }
158 if (isConstantIndex()) {
159 return SortCategory::ConstantIndex;
160 }
161 if (isConstantFelt()) {
162 return SortCategory::ConstantFelt;
163 }
164
165 llvm::errs() << *this << '\n';
166 llvm_unreachable("unhandled SourceRef sort category");
167}
168
169StringRef SourceRef::getTemplateConstantName() const {
170 auto constantVal = getConstant();
171 ensure(succeeded(constantVal), "template constant must be constant");
172 auto constRead = llvm::dyn_cast<ConstReadOp>(constantVal->getDefiningOp());
173 ensure(constRead, "template constant must be backed by const.read");
174 return constRead.getConstName();
175}
176
177std::strong_ordering
178SourceRef::compareWithinCategory(const SourceRef &rhs, SortCategory category) const {
179 switch (category) {
180 case SortCategory::BlockArgument: {
181 if (auto cmp = *getInputNum() <=> *rhs.getInputNum(); cmp != std::strong_ordering::equal) {
182 return cmp;
183 }
184 if (auto cmp = getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
185 cmp != std::strong_ordering::equal) {
186 return cmp;
187 }
188 return compareSourceRefPaths(getPath(), rhs.getPath());
189 }
190 case SortCategory::CreateStruct:
191 case SortCategory::NonDet:
192 case SortCategory::RootResult: {
193 if (auto cmp = getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
194 cmp != std::strong_ordering::equal) {
195 return cmp;
196 }
197 return compareSourceRefPaths(getPath(), rhs.getPath());
198 }
199 case SortCategory::TemplateConstant: {
200 if (auto cmp = compareStringRef(getTemplateConstantName(), rhs.getTemplateConstantName());
201 cmp != std::strong_ordering::equal) {
202 return cmp;
203 }
204 return getAsOpaquePointer() <=> rhs.getAsOpaquePointer();
205 }
206 case SortCategory::ConstantIndex:
207 return compareDynamicAPInt(*getConstantIndexValue(), *rhs.getConstantIndexValue());
208 case SortCategory::ConstantFelt:
209 return compareDynamicAPInt(*getConstantFeltValue(), *rhs.getConstantFeltValue());
210 }
211
212 llvm_unreachable("unhandled SourceRef category compare");
213}
214
222SymbolLookupResult<StructDefOp>
223getStructDef(SymbolTableCollection &tables, ModuleOp mod, StructType ty) {
224 auto sDef = ty.getDefinition(tables, mod);
225 ensure(
226 succeeded(sDef),
227 "could not find '" + StructDefOp::getOperationName() + "' op from struct type"
228 );
229
230 return std::move(*sDef);
231}
232
233std::vector<SourceRef>
234SourceRef::getAllSourceRefs(SymbolTableCollection &tables, ModuleOp mod, const SourceRef &root) {
235 std::vector<SourceRef> res = {root};
236 for (const SourceRef &child : root.getAllChildren(tables, mod)) {
237 auto recursiveChildren = getAllSourceRefs(tables, mod, child);
238 res.insert(res.end(), recursiveChildren.begin(), recursiveChildren.end());
239 }
240 return res;
241}
242
243std::vector<SourceRef> SourceRef::getAllSourceRefs(StructDefOp structDef, FuncDefOp fnOp) {
244 std::vector<SourceRef> res;
245
246 ensure(
247 structDef == fnOp->getParentOfType<StructDefOp>(), "function must be within the given struct"
248 );
249
250 FailureOr<ModuleOp> modOp = getRootModule(structDef);
251 ensure(succeeded(modOp), "could not lookup module from struct " + Twine(structDef.getName()));
252
253 SymbolTableCollection tables;
254 for (auto a : fnOp.getArguments()) {
255 auto argRes = getAllSourceRefs(tables, modOp.value(), SourceRef(a));
256 res.insert(res.end(), argRes.begin(), argRes.end());
257 }
258
259 // For compute functions, the "self" member is not arg0 like for constrain, but
260 // rather the struct value returned from the function.
261 if (fnOp.isStructCompute()) {
262 Value selfVal = fnOp.getSelfValueFromCompute();
263 auto createOp = dyn_cast_if_present<CreateStructOp>(selfVal.getDefiningOp());
264 ensure(createOp, "self value should originate from struct.new operation");
265 auto selfRes = getAllSourceRefs(tables, modOp.value(), SourceRef(createOp));
266 res.insert(res.end(), selfRes.begin(), selfRes.end());
267 }
268
269 return res;
270}
271
272std::vector<SourceRef> SourceRef::getAllSourceRefs(StructDefOp structDef, MemberDefOp memberDef) {
273 std::vector<SourceRef> res;
274 FuncDefOp constrainFnOp = structDef.getConstrainFuncOp();
275 ensure(
276 memberDef->getParentOfType<StructDefOp>() == structDef,
277 "Member " + Twine(memberDef.getName()) + " is not a member of struct " +
278 Twine(structDef.getName())
279 );
280 FailureOr<ModuleOp> modOp = getRootModule(structDef);
281 ensure(succeeded(modOp), "could not lookup module from struct " + Twine(structDef.getName()));
282
283 // Get the self argument (like `FuncDefOp::getSelfValueFromConstrain()`)
284 BlockArgument self = constrainFnOp.getArguments().front();
285 SourceRef memberRef = SourceRef(self, {SourceRefIndex(memberDef)});
286
287 SymbolTableCollection tables;
288 return getAllSourceRefs(tables, modOp.value(), memberRef);
289}
290
291Type SourceRef::getType() const {
292 auto pathRef = getPath();
293 size_t arrayDerefs = 0;
294 size_t idx = pathRef.size();
295 while (idx > 0 && (pathRef[idx - 1].isIndex() || pathRef[idx - 1].isIndexRange())) {
296 arrayDerefs++;
297 idx--;
298 }
299
300 Type currTy = idx > 0 ? pathRef[idx - 1].getMember().getType() : value.getType();
301 if (arrayDerefs > 0) {
302 auto arrTy = dyn_cast<ArrayType>(currTy);
303 ensure(static_cast<bool>(arrTy), "SourceRef array indices require an array-typed base");
304 ensure(
305 arrayDerefs <= arrTy.getDimensionSizes().size(),
306 "SourceRef indexes more array dimensions than exist in the base type"
307 );
308
309 if (arrayDerefs == arrTy.getDimensionSizes().size()) {
310 currTy = arrTy.getElementType();
311 } else {
312 currTy =
313 ArrayType::get(arrTy.getElementType(), arrTy.getDimensionSizes().drop_front(arrayDerefs));
314 }
315 }
316 return currTy;
317}
318
319bool SourceRef::isValidPrefix(const SourceRef &prefix) const {
320 if (isConstant() || prefix.isConstant()) {
321 return false;
322 }
323
324 auto pathRef = getPath();
325 auto prefixPath = prefix.getPath();
326 if (value != prefix.value || pathRef.size() < prefixPath.size()) {
327 return false;
328 }
329 for (size_t i = 0; i < prefixPath.size(); i++) {
330 if (pathRef[i] != prefixPath[i]) {
331 return false;
332 }
333 }
334 return true;
335}
336
337FailureOr<SourceRef::Path> SourceRef::getSuffix(const SourceRef &prefix) const {
338 if (!isValidPrefix(prefix)) {
339 return failure();
340 }
341 Path suffix;
342 auto pathRef = getPath();
343 auto prefixPath = prefix.getPath();
344 suffix.reserve(pathRef.size() - prefixPath.size());
345 for (size_t i = prefixPath.size(); i < pathRef.size(); i++) {
346 suffix.push_back(pathRef[i]);
347 }
348 return suffix;
349}
350
351FailureOr<SourceRef> SourceRef::translate(const SourceRef &prefix, const SourceRef &other) const {
352 if (isConstant()) {
353 return *this;
354 }
355 auto suffix = getSuffix(prefix);
356 if (failed(suffix)) {
357 return failure();
358 }
359
360 SourceRef newSignalUsage = other; // copy
361 if (newSignalUsage.isRooted()) {
362 SourceRef::Path &pathRef = newSignalUsage.getPathMut();
363 pathRef.insert(pathRef.end(), suffix->begin(), suffix->end());
364 }
365
366 return newSignalUsage;
367}
368
369std::vector<SourceRef> getAllChildren(
370 SymbolTableCollection & /*tables*/, ModuleOp /*mod*/, ArrayType arrayTy, const SourceRef &root
371) {
372 std::vector<SourceRef> res;
373 // Recurse into arrays by iterating over their elements
374 for (int64_t i = 0; i < arrayTy.getDimSize(0); i++) {
375 auto childRef = root.createChild(SourceRefIndex(i));
376 ensure(succeeded(childRef), "array children require a rooted SourceRef");
377 res.push_back(*childRef);
378 }
379
380 return res;
381}
382
383std::vector<SourceRef> getAllChildren(
384 SymbolTableCollection &tables, ModuleOp mod, SymbolLookupResult<StructDefOp> structDefRes,
385 const SourceRef &root
386) {
387 std::vector<SourceRef> res;
388 // Recurse into struct types by iterating over all their member definitions
389 for (auto f : structDefRes.get().getOps<MemberDefOp>()) {
390 // We want to store the MemberDefOp, but without the possibility of accidentally dropping the
391 // reference, so we need to re-lookup the symbol to create a SymbolLookupResult, which will
392 // manage the external module containing the member defs, if needed.
393 // TODO: It would be nice if we could manage module op references differently
394 // so we don't have to do this.
395 auto structDefCopy = structDefRes;
396 auto memberLookup = lookupSymbolIn<MemberDefOp>(
397 tables, SymbolRefAttr::get(f.getContext(), f.getSymNameAttr()), std::move(structDefCopy),
398 mod.getOperation()
399 );
400 ensure(succeeded(memberLookup), "could not get SymbolLookupResult of existing MemberDefOp");
401 auto childRef = root.createChild(SourceRefIndex(memberLookup.value()));
402 ensure(succeeded(childRef), "struct children require a rooted SourceRef");
403 // Make a reference to the current member, regardless of if it is a composite
404 // type or not.
405 res.push_back(*childRef);
406 }
407 return res;
408}
409
410std::vector<SourceRef>
411SourceRef::getAllChildren(SymbolTableCollection &tables, ModuleOp mod) const {
412 auto ty = getType();
413 if (auto structTy = dyn_cast<StructType>(ty)) {
414 return llzk::getAllChildren(tables, mod, getStructDef(tables, mod, structTy), *this);
415 } else if (auto arrayType = dyn_cast<ArrayType>(ty)) {
416 return llzk::getAllChildren(tables, mod, arrayType, *this);
417 }
418 // Scalar type, no children
419 return {};
420}
421
422void SourceRef::print(raw_ostream &os) const {
423 if (isConstantFelt()) {
424 os << "<felt.const: " << *getConstantFeltValue() << '>';
425 } else if (isConstantIndex()) {
426 os << "<index: " << *getConstantIndexValue() << '>';
427 } else if (isTemplateConstant()) {
428 auto constRead = getDefiningOp<ConstReadOp>();
429 ensure(succeeded(constRead), "template constant should be backed by a const.read op");
430 auto structDefOp = (*constRead)->getParentOfType<StructDefOp>();
431 ensure(structDefOp, "struct template should have a struct parent");
432 os << '@' << structDefOp.getName() << "<[@" << constRead->getConstName() << "]>";
433 } else {
434 if (isCreateStructOp()) {
435 os << "%self";
436 } else if (isBlockArgument()) {
437 os << "%arg" << *getInputNum();
438 } else if (isNonDetOp()) {
439 os << '<' << *getNonDetOp() << '>';
440 } else if (isCallResult()) {
441 auto callOp = *getCallOp();
442 os << "<call " << callOp.getCallee();
443 os << ' ';
444 Operation *printScope = callOp.getOperation();
445 if (auto funcOp = callOp->getParentOfType<FuncDefOp>()) {
446 printScope = funcOp.getOperation();
447 }
448 // Allows us to print the SSA result value of the call to disambiguate
449 // repeated calls in the same function.
450 AsmState state(printScope);
451 value.printAsOperand(os, state);
452 os << '>';
453 } else {
454 ensure(isRooted(), "unhandled print case");
455 OpPrintingFlags flags;
456 value.printAsOperand(os, flags);
457 }
458
459 for (const auto &f : getPath()) {
460 os << "[" << f << "]";
461 }
462 }
463}
464
465bool SourceRef::operator==(const SourceRef &rhs) const {
466 // This way two felt constants can be equal even if the declared in separate ops.
467 if (isConstantInt() && rhs.isConstantInt()) {
468 DynamicAPInt lhsVal = *getConstantValue(), rhsVal = *rhs.getConstantValue();
469 return getType() == rhs.getType() && lhsVal == rhsVal;
470 }
471 return constant == rhs.constant && value == rhs.value && llvm::equal(getPath(), rhs.getPath());
472}
473
474// required for EquivalenceClasses usage
475std::strong_ordering SourceRef::operator<=>(const SourceRef &rhs) const {
476 auto lhsCategory = getSortCategory();
477 auto rhsCategory = rhs.getSortCategory();
478 if (auto cmp = lhsCategory <=> rhsCategory; cmp != std::strong_ordering::equal) {
479 return cmp;
480 }
481 return compareWithinCategory(rhs, lhsCategory);
482}
483
484size_t SourceRef::Hash::operator()(const SourceRef &val) const {
485 if (val.isConstantInt()) {
486 return llvm::hash_combine(val.getType(), *val.getConstantValue());
487 } else if (val.isTemplateConstant()) {
488 return llvm::hash_value(val.getAsOpaquePointer());
489 } else {
490 ensure(
491 val.isBlockArgument() || val.isCreateStructOp() || val.isNonDetOp() || val.isRooted(),
492 "unhandled SourceRef hash case"
493 );
494
495 size_t hash = llvm::hash_value(val.getAsOpaquePointer());
496 for (const auto &f : val.getPath()) {
497 hash = llvm::hash_combine(hash, f.getHash());
498 }
499 return hash;
500 }
501}
502
503raw_ostream &operator<<(raw_ostream &os, const SourceRef &rhs) {
504 rhs.print(os);
505 return os;
506}
507
508/* SourceRefSet */
509
511 insert(rhs.begin(), rhs.end());
512 return *this;
513}
514
515raw_ostream &operator<<(raw_ostream &os, const SourceRefSet &rhs) {
516 os << "{ ";
517 std::vector<SourceRef> sortedRefs(rhs.begin(), rhs.end());
518 std::sort(sortedRefs.begin(), sortedRefs.end());
519 for (auto it = sortedRefs.begin(); it != sortedRefs.end();) {
520 os << *it;
521 it++;
522 if (it != sortedRefs.end()) {
523 os << ", ";
524 } else {
525 os << ' ';
526 }
527 }
528 os << '}';
529 return os;
530}
531
532} // namespace llzk
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
Definition SourceRef.cpp:88
bool isIndexRange() const
Definition SourceRef.h:73
bool isIndex() const
Definition SourceRef.h:67
bool isMember() const
Definition SourceRef.h:55
llvm::DynamicAPInt getIndex() const
Definition SourceRef.h:68
void print(mlir::raw_ostream &os) const
Definition SourceRef.cpp:73
IndexRange getIndexRange() const
Definition SourceRef.h:74
component::MemberDefOp getMember() const
Definition SourceRef.h:59
SourceRefIndex(component::MemberDefOp f)
Definition SourceRef.h:46
SourceRefSet & join(const SourceRefSet &rhs)
A reference to a "source", which is the base value from which other SSA values are derived.
Definition SourceRef.h:132
bool isBlockArgument() const
Definition SourceRef.h:243
mlir::FailureOr< SourceRef > createChild(const SourceRefIndex &r) const
Definition SourceRef.h:339
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
Definition SourceRef.h:279
void print(mlir::raw_ostream &os) const
bool isCallResult() const
Definition SourceRef.h:278
bool operator==(const SourceRef &rhs) const
bool isConstantFelt() const
Definition SourceRef.h:220
bool isRooted() const
Definition SourceRef.h:242
llvm::ArrayRef< SourceRefIndex > getPath() const
Definition SourceRef.h:361
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
Definition SourceRef.h:281
bool isConstantIndex() const
Definition SourceRef.h:223
std::vector< SourceRefIndex > Path
Definition SourceRef.h:134
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
Definition SourceRef.h:296
mlir::FailureOr< unsigned > getInputNum() const
Definition SourceRef.h:262
mlir::FailureOr< NonDetOp > getNonDetOp() const
Definition SourceRef.h:276
mlir::FailureOr< llvm::DynamicAPInt > getConstantIndexValue() const
Definition SourceRef.h:289
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 isNonDetOp() const
Definition SourceRef.h:275
bool isTemplateConstant() const
Definition SourceRef.h:227
bool isConstant() const
Definition SourceRef.h:231
bool isConstantInt() const
Definition SourceRef.h:232
bool isCreateStructOp() const
Definition SourceRef.h:270
mlir::Type getType() const
static ArrayType get(::mlir::Type elementType, ::llvm::ArrayRef<::mlir::Attribute > dimensionSizes)
Definition Types.cpp.inc:83
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:1165
::llzk::function::FuncDefOp getConstrainFuncOp()
Gets the FuncDefOp that defines the constrain function in this structure, if present,...
Definition Ops.cpp:470
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
Definition Types.cpp:26
::mlir::Value getSelfValueFromCompute()
Return the "self" value (i.e.
Definition Ops.cpp:363
bool isStructCompute()
Return true iff the function is within a StructDefOp and named FUNC_NAME_COMPUTE.
Definition Ops.h.inc:867
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)
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