LLZK 3.0.0
An open-source IR for Zero Knowledge (ZK) circuits
Loading...
Searching...
No Matches
Ops.cpp
Go to the documentation of this file.
1//===-- Ops.cpp - Func and call op implementations --------------*- 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// Copyright 2026 Project LLZK
7// SPDX-License-Identifier: Apache-2.0
8//
9// Adapted from the LLVM Project's lib/Dialect/Func/IR/FuncOps.cpp
10// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
11// See https://llvm.org/LICENSE.txt for license information.
12// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
13//
14//===----------------------------------------------------------------------===//
15
17
27#include "llzk/Util/Compare.h"
33
34#include <mlir/IR/IRMapping.h>
35#include <mlir/IR/OpImplementation.h>
36#include <mlir/Interfaces/CallInterfaces.h>
37#include <mlir/Interfaces/FunctionImplementation.h>
38
39#include <llvm/ADT/DenseSet.h>
40#include <llvm/ADT/MapVector.h>
41
42// TableGen'd implementation files
43#define GET_OP_CLASSES
45
46using namespace mlir;
47using namespace llzk::component;
48using namespace llzk::polymorphic;
49
50namespace llzk::function {
51
52FunctionKind fnNameToKind(mlir::StringRef name) {
53 if (FUNC_NAME_COMPUTE == name) {
55 } else if (FUNC_NAME_CONSTRAIN == name) {
57 } else if (FUNC_NAME_PRODUCT == name) {
59 } else {
60 return FunctionKind::Free;
61 }
62}
63
64namespace {
66inline LogicalResult
67verifyTypeResolution(SymbolTableCollection &tables, Operation *origin, FunctionType funcType) {
69 tables, origin, ArrayRef<ArrayRef<Type>> {funcType.getInputs(), funcType.getResults()}
70 );
71}
72
77static LogicalResult verifyArgOrResNameAttrs(
78 ArrayAttr attrs, StringRef ownAttrName, StringRef crossAttrName, StringRef ownLabel,
79 StringRef crossLabel, EmitErrorFn emitFn
80) {
81 if (!attrs) {
82 return success();
83 }
84 llvm::DenseSet<StringAttr> seenNames;
85 for (auto [i, attr] : llvm::enumerate(attrs)) {
86 auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attr);
87 if (!dictAttr) {
88 continue;
89 }
90 if (dictAttr.contains(crossAttrName)) {
91 return emitFn().append(
92 '\'', crossAttrName, "' is only valid on function ", crossLabel, "s but found on ",
93 ownLabel, ' ', i
94 );
95 }
96 Attribute nameAttr = dictAttr.get(ownAttrName);
97 if (!nameAttr) {
98 continue;
99 }
100 auto name = llvm::dyn_cast<StringAttr>(nameAttr);
101 if (!name) {
102 return emitFn().append(
103 '\'', ownAttrName, "' on ", ownLabel, ' ', i, " must be a string attribute"
104 );
105 }
106 if (!llvm::isa<NoneType>(name.getType())) {
107 return emitFn().append(
108 '\'', ownAttrName, "' on ", ownLabel, ' ', i, " must not have an explicit type"
109 );
110 }
111 if (name.getValue().empty()) {
112 return emitFn().append('\'', ownAttrName, "' on ", ownLabel, ' ', i, " must not be empty");
113 }
114 if (!seenNames.insert(name).second) {
115 return emitFn().append(
116 "duplicate '", ownAttrName, "' value \"", name.getValue(), "\" on ", ownLabel, ' ', i
117 );
118 }
119 }
120 return success();
121}
122
123static std::optional<StringAttr>
124getFunctionNameAttrAtIndex(ArrayAttr attrs, unsigned index, StringRef attrName) {
125 if (!attrs || index >= attrs.size()) {
126 return std::nullopt;
127 }
128 if (auto dictAttr = llvm::dyn_cast<DictionaryAttr>(attrs[index])) {
129 if (auto nameAttr = llvm::dyn_cast_if_present<StringAttr>(dictAttr.get(attrName))) {
130 return nameAttr;
131 }
132 }
133 return std::nullopt;
134}
135} // namespace
136
137//===----------------------------------------------------------------------===//
138// FuncDefOp
139//===----------------------------------------------------------------------===//
140
142 Location location, StringRef name, FunctionType type, ArrayRef<NamedAttribute> attrs
143) {
144 return delegate_to_build<FuncDefOp>(location, name, type, attrs);
145}
146
148 Location location, StringRef name, FunctionType type, Operation::dialect_attr_range attrs
149) {
150 SmallVector<NamedAttribute, 8> attrRef(attrs);
151 return create(location, name, type, llvm::ArrayRef(attrRef));
152}
153
155 Location location, StringRef name, FunctionType type, ArrayRef<NamedAttribute> attrs,
156 ArrayRef<DictionaryAttr> argAttrs
157) {
158 FuncDefOp func = create(location, name, type, attrs);
159 func.setAllArgAttrs(argAttrs);
160 return func;
161}
162
164 OpBuilder &builder, OperationState &state, StringRef name, FunctionType type,
165 ArrayRef<NamedAttribute> attrs, ArrayRef<DictionaryAttr> argAttrs
166) {
167 state.addAttribute(SymbolTable::getSymbolAttrName(), builder.getStringAttr(name));
168 state.addAttribute(getFunctionTypeAttrName(state.name), TypeAttr::get(type));
169 state.attributes.append(attrs.begin(), attrs.end());
170 state.addRegion();
171
172 if (argAttrs.empty()) {
173 return;
174 }
175 assert(type.getNumInputs() == argAttrs.size());
176 call_interface_impl::addArgAndResultAttrs(
177 builder, state, argAttrs, /*resultAttrs=*/ {}, getArgAttrsAttrName(state.name),
178 getResAttrsAttrName(state.name)
179 );
180}
181
182ParseResult FuncDefOp::parse(OpAsmParser &parser, OperationState &result) {
183 auto buildFuncType = [](Builder &builder, ArrayRef<Type> argTypes, ArrayRef<Type> results,
184 function_interface_impl::VariadicFlag,
185 std::string &) { return builder.getFunctionType(argTypes, results); };
186
187 return function_interface_impl::parseFunctionOp(
188 parser, result, /*allowVariadic=*/false, getFunctionTypeAttrName(result.name), buildFuncType,
189 getArgAttrsAttrName(result.name), getResAttrsAttrName(result.name)
190 );
191}
192
193void FuncDefOp::print(OpAsmPrinter &p) {
194 function_interface_impl::printFunctionOp(
195 p, *this, /*isVariadic=*/false, getFunctionTypeAttrName(), getArgAttrsAttrName(),
197 );
198}
199
202void FuncDefOp::cloneInto(FuncDefOp dest, IRMapping &mapper) {
203 // Add the attributes of this function to dest.
204 llvm::MapVector<StringAttr, Attribute> newAttrMap;
205 for (const auto &attr : dest->getAttrs()) {
206 newAttrMap.insert({attr.getName(), attr.getValue()});
207 }
208 for (const auto &attr : (*this)->getAttrs()) {
209 newAttrMap.insert({attr.getName(), attr.getValue()});
210 }
211
212 auto newAttrs =
213 llvm::to_vector(llvm::map_range(newAttrMap, [](std::pair<StringAttr, Attribute> attrPair) {
214 return NamedAttribute(attrPair.first, attrPair.second);
215 }));
216 dest->setAttrs(DictionaryAttr::get(getContext(), newAttrs));
217
218 // Clone the body.
219 getBody().cloneInto(&dest.getBody(), mapper);
220}
221
227FuncDefOp FuncDefOp::clone(IRMapping &mapper) {
228 // Create the new function.
229 FuncDefOp newFunc = llvm::cast<FuncDefOp>(getOperation()->cloneWithoutRegions());
230
231 // If the function has a body, then the user might be deleting arguments to
232 // the function by specifying them in the mapper. If so, we don't add the
233 // argument to the input type vector.
234 if (!isExternal()) {
235 FunctionType oldType = getFunctionType();
236
237 unsigned oldNumArgs = oldType.getNumInputs();
238 SmallVector<Type, 4> newInputs;
239 newInputs.reserve(oldNumArgs);
240 for (unsigned i = 0; i != oldNumArgs; ++i) {
241 if (!mapper.contains(getArgument(i))) {
242 newInputs.push_back(oldType.getInput(i));
243 }
244 }
245
248 if (newInputs.size() != oldNumArgs) {
249 newFunc.setType(FunctionType::get(oldType.getContext(), newInputs, oldType.getResults()));
250
251 if (ArrayAttr argAttrs = getAllArgAttrs()) {
252 SmallVector<Attribute> newArgAttrs;
253 newArgAttrs.reserve(newInputs.size());
254 for (unsigned i = 0; i != oldNumArgs; ++i) {
255 if (!mapper.contains(getArgument(i))) {
256 newArgAttrs.push_back(argAttrs[i]);
257 }
258 }
259 newFunc.setAllArgAttrs(newArgAttrs);
260 }
261 }
262 }
263
265 cloneInto(newFunc, mapper);
266 return newFunc;
267}
268
270 IRMapping mapper;
271 return clone(mapper);
272}
273
275 if (newValue) {
276 getOperation()->setAttr(AllowConstraintAttr::name, UnitAttr::get(getContext()));
277 } else {
278 getOperation()->removeAttr(AllowConstraintAttr::name);
279 }
280}
281
283 if (newValue) {
284 getOperation()->setAttr(AllowWitnessAttr::name, UnitAttr::get(getContext()));
285 } else {
286 getOperation()->removeAttr(AllowWitnessAttr::name);
287 }
288}
289
291 if (newValue) {
292 getOperation()->setAttr(AllowNonNativeFieldOpsAttr::name, UnitAttr::get(getContext()));
293 } else {
294 getOperation()->removeAttr(AllowNonNativeFieldOpsAttr::name);
295 }
296}
297
299 if (newValue) {
300 getOperation()->setAttr(AllowVerifOpsAttr::name, UnitAttr::get(getContext()));
301 } else {
302 getOperation()->removeAttr(AllowVerifOpsAttr::name);
303 }
304}
305
306bool FuncDefOp::hasArgPublicAttr(unsigned index) {
307 if (index < this->getNumArguments()) {
308 DictionaryAttr res = function_interface_impl::getArgAttrDict(*this, index);
309 return res ? res.contains(PublicAttr::name) : false;
310 } else {
311 // TODO: print error? requested attribute for non-existant argument index
312 return false;
313 }
314}
315
316bool FuncDefOp::hasArgName(unsigned index) { return static_cast<bool>(getArgNameAttr(index)); }
317
318std::optional<StringAttr> FuncDefOp::getArgNameAttr(unsigned index) {
319 return getFunctionNameAttrAtIndex(getAllArgAttrs(), index, ARG_NAME_ATTR_NAME);
320}
321
322void FuncDefOp::setArgNameAttr(unsigned index, const StringAttr &attr) {
323 assert(index < getNumArguments() && "argument index out of range");
324 setArgAttr(index, ARG_NAME_ATTR_NAME, attr);
325}
326
327void FuncDefOp::setArgName(unsigned index, StringRef name) {
328 setArgNameAttr(index, StringAttr::get(getContext(), name));
329}
330
331bool FuncDefOp::hasResName(unsigned index) { return static_cast<bool>(getResNameAttr(index)); }
332
333std::optional<StringAttr> FuncDefOp::getResNameAttr(unsigned index) {
334 return getFunctionNameAttrAtIndex(getAllResultAttrs(), index, RES_NAME_ATTR_NAME);
335}
336
337void FuncDefOp::setResNameAttr(unsigned index, const StringAttr &attr) {
338 assert(index < getNumResults() && "result index out of range");
339 setResultAttr(index, RES_NAME_ATTR_NAME, attr);
340}
341
342void FuncDefOp::setResName(unsigned index, StringRef name) {
343 setResNameAttr(index, StringAttr::get(getContext(), name));
344}
345
346LogicalResult FuncDefOp::verify() {
347 OwningEmitErrorFn emitErrorFunc = getEmitOpErrFn(this);
348
349 if ((*this)->hasAttr(ARG_NAME_ATTR_NAME)) {
350 return emitErrorFunc() << '\'' << ARG_NAME_ATTR_NAME << "' is only valid on function arguments";
351 }
352 if ((*this)->hasAttr(RES_NAME_ATTR_NAME)) {
353 return emitErrorFunc() << '\'' << RES_NAME_ATTR_NAME << "' is only valid on function results";
354 }
355
356 if (failed(verifyArgOrResNameAttrs(
357 getAllResultAttrs(), RES_NAME_ATTR_NAME, ARG_NAME_ATTR_NAME, "result", "argument",
358 emitErrorFunc
359 ))) {
360 return failure();
361 }
362
363 if (failed(verifyArgOrResNameAttrs(
364 getAllArgAttrs(), ARG_NAME_ATTR_NAME, RES_NAME_ATTR_NAME, "argument", "result",
365 emitErrorFunc
366 ))) {
367 return failure();
368 }
369
370 // Ensure that only valid LLZK types are used for arguments and return. Additionally, the struct
371 // functions may not use AffineMapAttrs in their parameter types. If such a scenario seems to make
372 // sense when generating LLZK IR, it's likely better to introduce a struct parameter to use
373 // instead and instantiate the struct with that AffineMapAttr.
374 FunctionType type = getFunctionType();
375 for (Type t : type.getInputs()) {
376 if (llzk::checkValidType(emitErrorFunc, t).failed()) {
377 return failure();
378 }
379 if (isInStruct() && hasAffineMapAttr(t)) {
380 return emitErrorFunc().append(
381 "\"@", getName(), "\" parameters cannot contain affine map attributes but found ", t
382 );
383 }
384 }
385 for (Type t : type.getResults()) {
386 if (llzk::checkValidType(emitErrorFunc, t).failed()) {
387 return failure();
388 }
389 }
390 // Ensure that the function does not contain nested modules.
391 // Functions also cannot contain nested structs, but this check is handled
392 // via struct.def's requirement of having module as a parent.
393 WalkResult res = this->walk<WalkOrder::PreOrder>([this](ModuleOp nestedMod) {
394 getEmitOpErrFn(nestedMod)().append(
395 "cannot be nested within '", getOperation()->getName(), "' operations"
396 );
397 return WalkResult::interrupt();
398 });
399 if (res.wasInterrupted()) {
400 return failure();
401 }
402
403 return success();
404}
405
406namespace {
407
408LogicalResult
409verifyFuncTypeCompute(FuncDefOp &origin, SymbolTableCollection &tables, StructDefOp &parent) {
410 FunctionType funcType = origin.getFunctionType();
411 llvm::ArrayRef<Type> resTypes = funcType.getResults();
412 // Must return type of parent struct
413 if (resTypes.size() != 1) {
414 return origin.emitOpError().append(
415 "\"@", FUNC_NAME_COMPUTE, "\" must have exactly one return type"
416 );
417 }
418 if (failed(checkSelfType(tables, parent, resTypes.front(), origin, "return"))) {
419 return failure();
420 }
421
422 // After the more specific checks (to ensure more specific error messages would be produced if
423 // necessary), do the general check that all symbol references in the types are valid. The return
424 // types were already checked so just check the input types.
425 return llzk::verifyTypeResolution(tables, origin, funcType.getInputs());
426}
427
428LogicalResult
429verifyFuncTypeProduct(FuncDefOp &origin, SymbolTableCollection &tables, StructDefOp &parent) {
430 // The signature for @product is the same as the signature for @compute
431 return verifyFuncTypeCompute(origin, tables, parent);
432}
433
434LogicalResult
435verifyFuncTypeConstrain(FuncDefOp &origin, SymbolTableCollection &tables, StructDefOp &parent) {
436 FunctionType funcType = origin.getFunctionType();
437 // Must return '()' type, i.e., have no return types
438 if (funcType.getResults().size() != 0) {
439 return origin.emitOpError() << "\"@" << FUNC_NAME_CONSTRAIN << "\" must have no return type";
440 }
441
442 // Type of the first parameter must match the parent StructDefOp of the current operation.
443 llvm::ArrayRef<Type> inputTypes = funcType.getInputs();
444 if (inputTypes.size() < 1) {
445 return origin.emitOpError() << "\"@" << FUNC_NAME_CONSTRAIN
446 << "\" must have at least one input type";
447 }
448 if (failed(checkSelfType(tables, parent, inputTypes.front(), origin, "first input"))) {
449 return failure();
450 }
451
452 // After the more specific checks (to ensure more specific error messages would be produced if
453 // necessary), do the general check that all symbol references in the types are valid. There are
454 // no return types, just check the remaining input types (the first was already checked via
455 // the checkSelfType() call above).
456 return llzk::verifyTypeResolution(tables, origin, inputTypes.drop_front());
457}
458
459} // namespace
460
461LogicalResult FuncDefOp::verifySymbolUses(SymbolTableCollection &tables) {
462 // Additional checks for the compute/constrain/product functions within a struct
463 if (StructDefOp parentStructOpt = getParentOfType<StructDefOp>(*this)) {
464 // Verify return type restrictions for functions within a StructDefOp
465 if (nameIsCompute()) {
466 return verifyFuncTypeCompute(*this, tables, parentStructOpt);
467 } else if (nameIsConstrain()) {
468 return verifyFuncTypeConstrain(*this, tables, parentStructOpt);
469 } else if (nameIsProduct()) {
470 return verifyFuncTypeProduct(*this, tables, parentStructOpt);
471 }
472 }
473 // In the general case, verify symbol resolution in all input and output types.
474 return verifyTypeResolution(tables, *this, getFunctionType());
475}
476
477SymbolRefAttr FuncDefOp::getFullyQualifiedName(bool requireParent) {
478 return llzk::getFullyQualifiedName(*this, requireParent);
479}
480
481namespace {
482
483static Value getSelfValueFromWitnessGen(FuncDefOp funcDefOp, Twine name) {
484 // Get the single block of the function body
485 Region &body = funcDefOp.getBody();
486 ensure(!body.empty(), name + "() function body must not be empty");
487
488 // The terminator should be the return op
489 Operation *terminator = body.back().getTerminator();
490 ensure(terminator, name + "() function must have a terminator op");
491 auto retOp = llvm::dyn_cast<ReturnOp>(terminator);
492 if (!retOp) {
493 llvm::errs() << "Expected '" << ReturnOp::getOperationName() << "' but found '"
494 << terminator->getName() << "'\n";
495 ensure(false, name + "() function must end with ReturnOp");
496 }
497 return retOp.getOperands().front();
498}
499
500} // namespace
501
503 assert(nameIsCompute()); // skip inStruct check to allow dangling functions
504 return getSelfValueFromWitnessGen(*this, "compute");
505}
506
508 assert(nameIsProduct()); // skip inStruct check to allow dangling functions
509 return getSelfValueFromWitnessGen(*this, "product");
510}
511
513 assert(nameIsConstrain()); // skip inStruct check to allow dangling functions
514 return getArguments().front();
515}
516
518 assert(isStructCompute() && "violated implementation pre-condition");
520}
521
522//===----------------------------------------------------------------------===//
523// ReturnOp
524//===----------------------------------------------------------------------===//
525
526LogicalResult ReturnOp::verify() {
527 auto function = getParentOp<FuncDefOp>(); // parent is FuncDefOp per ODS
528
529 // The operand number and types must match the function signature.
530 const auto results = function.getFunctionType().getResults();
531 if (getNumOperands() != results.size()) {
532 return emitOpError("has ") << getNumOperands() << " operands, but enclosing function (@"
533 << function.getName() << ") returns " << results.size();
534 }
535
536 for (unsigned i = 0, e = results.size(); i != e; ++i) {
537 if (!typesUnify(getOperand(i).getType(), results[i])) {
538 return emitError() << "type of return operand " << i << " (" << getOperand(i).getType()
539 << ") doesn't match function result type (" << results[i] << ')'
540 << " in function @" << function.getName();
541 }
542 }
543
544 return success();
545}
546
547//===----------------------------------------------------------------------===//
548// CallOp
549//===----------------------------------------------------------------------===//
550
551// Custom implementation to deserialize bytecode produced prior to version 2 which added optional
552// `OptionalAttr<ArrayAttr>:$templateParams`.
553LogicalResult CallOp::readProperties(DialectBytecodeReader &reader, OperationState &state) {
554 auto &prop = state.getOrAddProperties<Properties>();
555 if (failed(reader.readAttribute(prop.callee)) ||
556 failed(reader.readAttribute(prop.mapOpGroupSizes)) ||
557 failed(reader.readOptionalAttribute(prop.numDimsPerMap))) {
558 return failure();
559 }
560
561 if (reader.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {
562 auto &propStorage = prop.operandSegmentSizes;
563 DenseI32ArrayAttr attr;
564 if (failed(reader.readAttribute(attr))) {
565 return failure();
566 }
567 if (attr.size() > static_cast<int64_t>(sizeof(propStorage) / sizeof(int32_t))) {
568 return reader.emitError("size mismatch for operand/result_segment_size");
569 }
570 llvm::copy(ArrayRef<int32_t>(attr), propStorage.begin());
571 }
572
573 // The `templateParams` is only available in version 2 or later.
574 auto versionOpt = reader.getDialectVersion<FunctionDialect>();
575 if (succeeded(versionOpt)) {
576 const auto &ver = static_cast<const LLZKDialectVersion &>(**versionOpt);
577 if (ver.majorVersion >= 2) {
578 if (failed(reader.readOptionalAttribute(prop.templateParams))) {
579 return failure();
580 }
581 }
582 }
583
584 if (reader.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6) {
585 return reader.readSparseArray(MutableArrayRef(prop.operandSegmentSizes));
586 };
587 return success();
588}
589
590// Same as tablegen would generate to serialize current version IR.
591void CallOp::writeProperties(DialectBytecodeWriter &writer) {
592 auto &prop = getProperties();
593 writer.writeAttribute(prop.callee);
594 writer.writeAttribute(prop.mapOpGroupSizes);
595 writer.writeOptionalAttribute(prop.numDimsPerMap);
596
597 if (writer.getBytecodeVersion() < /*kNativePropertiesODSSegmentSize=*/6) {
598 auto &propStorage = prop.operandSegmentSizes;
599 writer.writeAttribute(DenseI32ArrayAttr::get(this->getContext(), propStorage));
600 }
601
602 writer.writeOptionalAttribute(prop.templateParams);
603
604 auto &propStorage = prop.operandSegmentSizes;
605 if (writer.getBytecodeVersion() >= /*kNativePropertiesODSSegmentSize=*/6) {
606 writer.writeSparseArray(ArrayRef(propStorage));
607 }
608}
609
610void CallOp::build(
611 OpBuilder &odsBuilder, OperationState &odsState, TypeRange resultTypes, SymbolRefAttr callee,
612 ValueRange argOperands, ArrayRef<Attribute> templateParams
613) {
614 odsState.addTypes(resultTypes);
615 odsState.addOperands(argOperands);
617 odsBuilder, odsState, llzk::checkedCast<int32_t>(argOperands.size())
618 );
619 props.setCallee(callee);
620 addTemplateParams<CallOp>(odsBuilder, props, templateParams);
621}
622
623void CallOp::build(
624 OpBuilder &odsBuilder, OperationState &odsState, TypeRange resultTypes, SymbolRefAttr callee,
625 ArrayRef<ValueRange> mapOperands, DenseI32ArrayAttr numDimsPerMap, ValueRange argOperands,
626 ArrayRef<Attribute> templateParams
627) {
628 odsState.addTypes(resultTypes);
629 odsState.addOperands(argOperands);
631 odsBuilder, odsState, mapOperands, numDimsPerMap,
632 llzk::checkedCast<int32_t>(argOperands.size())
633 );
634 props.setCallee(callee);
635 addTemplateParams<CallOp>(odsBuilder, props, templateParams);
636}
637
638namespace {
639
640struct CallOpVerifier {
641 CallOpVerifier(CallOp *c, FunctionKind tgtFuncKind) : callOp(c), tgtKind(tgtFuncKind) {}
642 CallOpVerifier(CallOp *c, StringRef tgtName) : CallOpVerifier(c, fnNameToKind(tgtName)) {}
643 virtual ~CallOpVerifier() = default;
644
645 LogicalResult verify() {
646 // Rather than immediately returning on failure, we check all verifier steps and aggregate to
647 // provide as many errors are possible in a single verifier run.
648 LogicalResult aggregateResult = success();
649 if (failed(verifyTargetAttributes())) {
650 aggregateResult = failure();
651 }
652 if (failed(verifyInputs())) {
653 aggregateResult = failure();
654 }
655 if (failed(verifyOutputs())) {
656 aggregateResult = failure();
657 }
658 if (failed(verifyTemplateParams())) {
659 aggregateResult = failure();
660 }
661 if (failed(verifyAffineMapParams())) {
662 aggregateResult = failure();
663 }
664 return aggregateResult;
665 }
666
667protected:
668 CallOp *callOp;
669 FunctionKind tgtKind;
670
671 virtual LogicalResult verifyTargetAttributes() = 0;
672 virtual LogicalResult verifyInputs() = 0;
673 virtual LogicalResult verifyOutputs() = 0;
674 virtual LogicalResult verifyTemplateParams() = 0;
675 virtual LogicalResult verifyAffineMapParams() = 0;
676
678 LogicalResult verifyTargetAttributesMatch(FuncDefOp target) {
679 LogicalResult aggregateRes = success();
680 if (FuncDefOp caller = (*callOp)->getParentOfType<FuncDefOp>()) {
681 auto emitAttrErr = [&](StringLiteral attrName) {
682 aggregateRes = callOp->emitOpError()
683 << "target '@" << target.getName() << "' has '" << attrName
684 << "' attribute, which is not specified by the caller '@" << caller.getName()
685 << '\'';
686 };
687
688 if (target.hasAllowConstraintAttr() && !caller.hasAllowConstraintAttr()) {
689 emitAttrErr(AllowConstraintAttr::name);
690 }
691 if (target.hasAllowWitnessAttr() && !caller.hasAllowWitnessAttr()) {
692 emitAttrErr(AllowWitnessAttr::name);
693 }
694 if (target.hasAllowNonNativeFieldOpsAttr() && !caller.hasAllowNonNativeFieldOpsAttr()) {
695 emitAttrErr(AllowNonNativeFieldOpsAttr::name);
696 }
697 }
698 return aggregateRes;
699 }
700
701 LogicalResult verifyNoTemplateInstantiations() {
702 if (!isNullOrEmpty(callOp->getTemplateParamsAttr())) {
703 // Tested in call_with_template_params_fail.llzk
704 return callOp->emitOpError().append(
705 "can only have template instantiations when targeting a templated free function"
706 );
707 }
708 return success();
709 }
710
711 LogicalResult verifyNoAffineMapInstantiations() {
712 if (!isNullOrEmpty(callOp->getMapOpGroupSizesAttr())) {
713 // Tested in call_with_affinemap_fail.llzk
714 return callOp->emitOpError().append(
715 "can only have affine map instantiations when targeting a \"@", FUNC_NAME_COMPUTE,
716 "\" function"
717 );
718 }
719 // ASSERT: the check above is sufficient due to VerifySizesForMultiAffineOps trait.
720 assert(isNullOrEmpty(callOp->getNumDimsPerMapAttr()));
721 assert(callOp->getMapOperands().empty());
722 return success();
723 }
724};
725
726struct KnownTargetVerifier : public CallOpVerifier {
727 KnownTargetVerifier(CallOp *c, SymbolLookupResult<FuncDefOp> &&tgtRes)
728 : CallOpVerifier(c, tgtRes.get().getSymName()), shared(c, tgtRes) {}
729
730 LogicalResult verifyTargetAttributes() override {
731 return CallOpVerifier::verifyTargetAttributesMatch(shared.getTarget());
732 }
733
734 LogicalResult verifyInputs() override {
735 return shared.verifyTypesMatch(
736 callOp->getArgOperands().getTypes(), shared.getTargetType().getInputs(), "operand"
737 );
738 }
739
740 LogicalResult verifyOutputs() override {
741 return shared.verifyTypesMatch(
742 callOp->getResultTypes(), shared.getTargetType().getResults(), "result"
743 );
744 }
745
746 LogicalResult verifyTemplateParams() override {
747 Operation *tgtOp = shared.getTarget().getOperation();
748 if (isInStruct(tgtOp)) {
749 // Struct function calls cannot contain template parameter instantiations.
750 return verifyNoTemplateInstantiations();
751 } else if (TemplateOp tgtOpParent = getParentOfType<TemplateOp>(tgtOp)) {
752 return shared.verifyTemplateInstantiation(tgtOpParent);
753 } else {
754 // Non-template functions cannot contain template parameter instantiations.
755 return verifyNoTemplateInstantiations();
756 }
757 }
758
759 LogicalResult verifyAffineMapParams() override {
760 if ((FunctionKind::StructCompute == tgtKind || FunctionKind::StructProduct == tgtKind) &&
761 isInStruct(shared.getTarget().getOperation())) {
762 // Return type should be a single StructType. If that is not the case here, just bail without
763 // producing an error. The combination of this KnownTargetVerifier resolving the callee to a
764 // specific FuncDefOp and verifyFuncTypeCompute() ensuring all FUNC_NAME_COMPUTE FuncOps have
765 // a single StructType return value will produce a more relevant error message in that case.
766 if (StructType retTy = callOp->getSingleResultTypeOfWitnessGen()) {
767 if (ArrayAttr params = retTy.getParams()) {
768 // Collect the struct parameters that are defined via AffineMapAttr
769 SmallVector<AffineMapAttr> mapAttrs;
770 for (Attribute a : params) {
771 if (AffineMapAttr m = dyn_cast<AffineMapAttr>(a)) {
772 mapAttrs.push_back(m);
773 }
774 }
776 callOp->getMapOperands(), callOp->getNumDimsPerMap(), mapAttrs, *callOp
777 );
778 }
779 }
780 return success();
781 } else {
782 // Global functions and constrain functions cannot have affine map instantiations.
783 return verifyNoAffineMapInstantiations();
784 }
785 }
786
787private:
788 llzk::KnownTargetVerifier<CallOp, FuncDefOp> shared;
789};
790
793LogicalResult checkSelfTypeUnknownTarget(
794 StringAttr expectedParamName, Type actualType, CallOp *origin, const char *aspect
795) {
796 if (!llvm::isa<TypeVarType>(actualType) ||
797 llvm::cast<TypeVarType>(actualType).getRefName() != expectedParamName) {
798 // Tested in function_restrictions_fail.llzk:
799 // Non-tvar for constrain input via "call_target_constrain_without_self_non_struct"
800 // Non-tvar for compute output via "call_target_compute_wrong_type_ret"
801 // Wrong tvar for constrain input via "call_target_constrain_without_self_wrong_tvar_param"
802 // Wrong tvar for compute output via "call_target_compute_wrong_tvar_param_ret"
803 return origin->emitOpError().append(
804 "target \"@", origin->getCallee().getLeafReference().getValue(), "\" expected ", aspect,
805 " type '!", TypeVarType::name, "<@", expectedParamName.getValue(), ">' but found ",
806 actualType
807 );
808 }
809 return success();
810}
811
821struct UnknownTargetVerifier : public CallOpVerifier {
822 UnknownTargetVerifier(CallOp *c, FunctionKind tgtFuncKind, SymbolRefAttr callee)
823 : CallOpVerifier(c, tgtFuncKind), calleeAttr(callee) {
824 assert(
825 tgtFuncKind == FunctionKind::StructCompute ||
826 tgtFuncKind == FunctionKind::StructConstrain || tgtFuncKind == FunctionKind::StructProduct
827 ); // pre-condition mentioned above
828 }
829
830 LogicalResult verifyTargetAttributes() override {
831 // Based on the precondition of this verifier, the target must be either a
832 // struct compute, constrain, or product function.
833 LogicalResult aggregateRes = success();
834 if (FuncDefOp caller = (*callOp)->getParentOfType<FuncDefOp>()) {
835 auto emitAttrErr = [&](StringLiteral attrName) {
836 aggregateRes = callOp->emitOpError()
837 << "target '" << calleeAttr << "' has '" << attrName
838 << "' attribute, which is not specified by the caller '@" << caller.getName()
839 << '\'';
840 };
841
842 switch (tgtKind) {
844 if (!caller.hasAllowConstraintAttr()) {
845 emitAttrErr(AllowConstraintAttr::name);
846 }
847 break;
849 if (!caller.hasAllowWitnessAttr()) {
850 emitAttrErr(AllowWitnessAttr::name);
851 }
852 break;
854 if (!caller.hasAllowWitnessAttr()) {
855 emitAttrErr(AllowWitnessAttr::name);
856 }
857 if (!caller.hasAllowConstraintAttr()) {
858 emitAttrErr(AllowConstraintAttr::name);
859 }
860 break;
861 default:
862 break;
863 }
864 }
865 return aggregateRes;
866 }
867
868 LogicalResult verifyInputs() override {
869 if (FunctionKind::StructCompute == tgtKind || FunctionKind::StructProduct == tgtKind) {
870 // Without known target, no additional checks can be done.
871 } else if (FunctionKind::StructConstrain == tgtKind) {
872 // Without known target, this can only check that the first input is VarType using the same
873 // struct parameter as the base of the callee (later replaced with the target struct's type).
874 Operation::operand_type_range inputTypes = callOp->getArgOperands().getTypes();
875 if (inputTypes.size() < 1) {
876 // Tested in function_restrictions_fail.llzk
877 return callOp->emitOpError()
878 << "target \"@" << FUNC_NAME_CONSTRAIN << "\" must have at least one input type";
879 }
880 return checkSelfTypeUnknownTarget(
881 calleeAttr.getRootReference(), inputTypes.front(), callOp, "first input"
882 );
883 }
884 return success();
885 }
886
887 LogicalResult verifyOutputs() override {
888 if (FunctionKind::StructCompute == tgtKind || FunctionKind::StructProduct == tgtKind) {
889 // Without known target, this can only check that the function returns VarType using the same
890 // struct parameter as the base of the callee (later replaced with the target struct's type).
891 Operation::result_type_range resTypes = callOp->getResultTypes();
892 if (resTypes.size() != 1) {
893 // Tested in function_restrictions_fail.llzk
894 return callOp->emitOpError().append(
895 "target \"@", FUNC_NAME_COMPUTE, "\" must have exactly one return type"
896 );
897 }
898 return checkSelfTypeUnknownTarget(
899 calleeAttr.getRootReference(), resTypes.front(), callOp, "return"
900 );
901 } else if (FunctionKind::StructConstrain == tgtKind) {
902 // Without known target, this can only check that the function has no return
903 if (callOp->getNumResults() != 0) {
904 // Tested in function_restrictions_fail.llzk
905 return callOp->emitOpError()
906 << "target \"@" << FUNC_NAME_CONSTRAIN << "\" must have no return type";
907 }
908 }
909 return success();
910 }
911
912 LogicalResult verifyTemplateParams() override {
913 // Struct function calls cannot contain template parameter instantiations.
914 return verifyNoTemplateInstantiations();
915 }
916
917 LogicalResult verifyAffineMapParams() override {
918 if (FunctionKind::StructCompute == tgtKind || FunctionKind::StructProduct == tgtKind) {
919 // Without known target, no additional checks can be done.
920 } else if (FunctionKind::StructConstrain == tgtKind) {
921 // Without known target, this can only check that there are no affine map instantiations.
922 return verifyNoAffineMapInstantiations();
923 }
924 return success();
925 }
926
927private:
928 SymbolRefAttr calleeAttr;
929};
930
931} // namespace
932
933LogicalResult CallOp::verifySymbolUses(SymbolTableCollection &tables) {
934 // First, verify symbol resolution in all input and output types.
935 if (failed(verifyTypeResolution(tables, *this, getTypeSignature()))) {
936 return failure(); // verifyTypeResolution() already emits a sufficient error message
937 }
938
939 // Check that the callee attribute was specified.
940 SymbolRefAttr calleeAttr = getCalleeAttr();
941 if (!calleeAttr) {
942 return emitOpError("requires a 'callee' symbol reference attribute");
943 }
944
945 // If the callee references a parameter of the template where this call appears, perform
946 // the subset of checks that can be done even though the target is unknown.
947 if (calleeAttr.getNestedReferences().size() == 1) {
948 if (TemplateOp parent = getParentOfType<TemplateOp>(*this)) {
949 if (parent.hasConstNamed<TemplateParamOp>(calleeAttr.getRootReference())) {
950 FunctionKind tgtKind = fnNameToKind(calleeAttr.getLeafReference().getValue());
951 if (tgtKind != FunctionKind::Free) {
952 return UnknownTargetVerifier(this, tgtKind, calleeAttr).verify();
953 }
954 return this->emitError("expected parameterized callee to target a struct function")
955 .append(
956 " (i.e. \"@", FUNC_NAME_PRODUCT, "\", \"@", FUNC_NAME_COMPUTE, "\", or \"@",
958 );
959 }
960 }
961 }
962
963 // Otherwise, callee must be specified via full path from the root module. Perform the full set of
964 // checks against the known target function.
965 auto tgtOpt = lookupTopLevelSymbol<FuncDefOp>(tables, calleeAttr, *this);
966 if (failed(tgtOpt)) {
967 return this->emitError() << "expected '" << FuncDefOp::getOperationName() << "' named \""
968 << calleeAttr << '"';
969 }
970 return KnownTargetVerifier(this, std::move(*tgtOpt)).verify();
971}
972
973namespace {
974
975bool calleeIsStructFunctionImpl(
976 const char *funcName, SymbolRefAttr callee, llvm::function_ref<StructType()> getType
977) {
978 if (callee.getLeafReference() == funcName) {
979 if (StructType t = getType()) {
980 // If the name ref within the StructType matches the `callee` prefix (i.e., sans the function
981 // name itself), then the `callee` target must be within a StructDefOp because validation
982 // checks elsewhere ensure that every StructType references a StructDefOp (i.e., the `callee`
983 // function is not simply a free function nested within a ModuleOp)
984 return t.getNameRef() == getPrefixAsSymbolRefAttr(callee);
985 }
986 }
987 return false;
988}
989
990} // namespace
991
993 return calleeIsStructFunctionImpl(FUNC_NAME_COMPUTE, getCallee(), [this]() {
994 return this->getSingleResultTypeOfCompute();
995 });
996}
997
999 return calleeIsStructFunctionImpl(FUNC_NAME_PRODUCT, getCallee(), [this]() {
1000 return this->getSingleResultTypeOfWitnessGen();
1001 });
1002}
1003
1005 return calleeIsStructFunctionImpl(FUNC_NAME_CONSTRAIN, getCallee(), [this]() {
1006 return getAtIndex<StructType>(this->getArgOperands().getTypes(), 0);
1007 });
1008}
1009
1011 assert(calleeIsStructCompute());
1012 return getResults().front();
1013}
1014
1016 assert(calleeIsStructProduct());
1017 return getResults().front();
1018}
1019
1021 assert(calleeIsStructConstrain());
1022 return getArgOperands().front();
1023}
1024
1028 } else if (calleeIsStructCompute()) {
1029 return getSelfValueFromCompute();
1030 } else if (calleeIsStructProduct()) {
1031 return getSelfValueFromProduct();
1032 } else {
1033 return nullptr;
1034 }
1035}
1036
1037FailureOr<SymbolLookupResult<FuncDefOp>> CallOp::getCalleeTarget(SymbolTableCollection &tables) {
1038 Operation *thisOp = this->getOperation();
1039 auto root = getRootModule(thisOp);
1040 assert(succeeded(root));
1041 return llzk::lookupSymbolIn<FuncDefOp>(tables, getCallee(), root->getOperation(), thisOp);
1042}
1043
1045 assert(calleeIsCompute() && "violated implementation pre-condition");
1046 return getIfSingleton<StructType>(getResultTypes());
1047}
1048
1050 assert(calleeContainsWitnessGen() && "violated implementation pre-condition");
1051 return getIfSingleton<StructType>(getResultTypes());
1052}
1053
1055CallInterfaceCallable CallOp::getCallableForCallee() { return getCalleeAttr(); }
1056
1058void CallOp::setCalleeFromCallable(CallInterfaceCallable callee) {
1059 setCalleeAttr(llvm::cast<SymbolRefAttr>(callee));
1060}
1061
1062Operation *CallOp::resolveCallableInTable(SymbolTableCollection *symbolTable) {
1063 FailureOr<SymbolLookupResult<FuncDefOp>> res =
1064 llzk::resolveCallable<FuncDefOp>(*symbolTable, *this);
1065 if (failed(res) || res->isManaged()) {
1066 // Cannot return pointer to a managed Operation since it would cause memory errors.
1067 return nullptr;
1068 }
1069 return res->get();
1070}
1071
1073 SymbolTableCollection tables;
1074 return resolveCallableInTable(&tables);
1075}
1076
1077} // namespace llzk::function
This file defines methods symbol lookup across LLZK operations and included files.
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::mlir::TypeRange resultTypes, ::mlir::SymbolRefAttr callee, ::mlir::ValueRange argOperands={}, ::llvm::ArrayRef<::mlir::Attribute > templateParams={})
bool calleeContainsWitnessGen()
Return true iff the callee function can contain witness generation code (this does not check if the c...
Definition Ops.h.inc:588
bool calleeIsStructConstrain()
Return true iff the callee function name is FUNC_NAME_CONSTRAIN within a StructDefOp.
Definition Ops.cpp:1004
::mlir::CallInterfaceCallable getCallableForCallee()
Return the callee of this operation.
Definition Ops.cpp:1055
::llzk::component::StructType getSingleResultTypeOfCompute()
Assuming the callee is FUNC_NAME_COMPUTE, return the single StructType result.
Definition Ops.cpp:1044
::mlir::SymbolRefAttr getCalleeAttr()
Definition Ops.h.inc:456
::mlir::Value getSelfValueFromProduct()
Return the "self" value (i.e.
Definition Ops.cpp:1015
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
Definition Ops.cpp:933
::mlir::Operation * resolveCallableInTable(::mlir::SymbolTableCollection *symbolTable)
Definition Ops.cpp:1062
::mlir::Value getSelfValue()
Definition Ops.cpp:1025
bool calleeIsStructCompute()
Return true iff the callee function name is FUNC_NAME_COMPUTE within a StructDefOp.
Definition Ops.cpp:992
::mlir::SymbolRefAttr getCallee()
Definition Ops.cpp.inc:566
::mlir::Operation * resolveCallable()
Definition Ops.cpp:1072
void writeProperties(::mlir::DialectBytecodeWriter &writer)
Definition Ops.cpp:591
bool calleeIsStructProduct()
Return true iff the callee function name is FUNC_NAME_PRODUCT within a StructDefOp.
Definition Ops.cpp:998
bool calleeIsCompute()
Return true iff the callee function name is FUNC_NAME_COMPUTE (this does not check if the callee func...
Definition Ops.h.inc:576
::llvm::ArrayRef< int32_t > getNumDimsPerMap()
Definition Ops.cpp.inc:576
::mlir::Operation::operand_range getArgOperands()
Definition Ops.h.inc:430
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
Definition Ops.cpp:1020
::mlir::OperandRangeRange getMapOperands()
Definition Ops.h.inc:434
::llzk::component::StructType getSingleResultTypeOfWitnessGen()
Assuming the callee contains witness generation code, return the single StructType result.
Definition Ops.cpp:1049
FoldAdaptor::Properties Properties
Definition Ops.h.inc:350
void setCalleeAttr(::mlir::SymbolRefAttr attr)
Definition Ops.h.inc:486
::llvm::LogicalResult readProperties(::mlir::DialectBytecodeReader &reader, ::mlir::OperationState &state)
Definition Ops.cpp:553
::mlir::Value getSelfValueFromCompute()
Return the "self" value (i.e.
Definition Ops.cpp:1010
void setCalleeFromCallable(::mlir::CallInterfaceCallable callee)
Set the callee for this operation.
Definition Ops.cpp:1058
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
Definition Ops.cpp:1037
void setAllowWitnessAttr(bool newValue=true)
Add (resp. remove) the allow_witness attribute to (resp. from) the function def.
Definition Ops.cpp:282
static FuncDefOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::llvm::StringRef name, ::mlir::FunctionType type, ::llvm::ArrayRef<::mlir::NamedAttribute > attrs={}, ::llvm::ArrayRef<::mlir::DictionaryAttr > argAttrs={})
Definition Ops.cpp.inc:1252
void setArgNameAttr(unsigned index, const ::mlir::StringAttr &attr)
Set the function.arg_name attribute for the argument at the given index.
Definition Ops.cpp:322
void setArgName(unsigned index, ::llvm::StringRef name)
Set the function.arg_name attribute for the argument at the given index from a string.
Definition Ops.cpp:327
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
Definition Ops.cpp:461
::mlir::Value getSelfValueFromCompute()
Return the "self" value (i.e.
Definition Ops.cpp:502
::mlir::FunctionType getFunctionType()
Definition Ops.cpp.inc:1229
static void build(::mlir::OpBuilder &odsBuilder, ::mlir::OperationState &odsState, ::llvm::StringRef name, ::mlir::FunctionType type, ::llvm::ArrayRef<::mlir::NamedAttribute > attrs={}, ::llvm::ArrayRef<::mlir::DictionaryAttr > argAttrs={})
void setAllowNonNativeFieldOpsAttr(bool newValue=true)
Add (resp. remove) the allow_non_native_field_ops attribute to (resp. from) the function def.
Definition Ops.cpp:290
void setResNameAttr(unsigned index, const ::mlir::StringAttr &attr)
Set the function.res_name attribute for the result at the given index.
Definition Ops.cpp:337
::mlir::StringAttr getFunctionTypeAttrName()
Definition Ops.h.inc:823
bool hasAllowNonNativeFieldOpsAttr()
Return true iff the function def has the allow_non_native_field_ops attribute.
Definition Ops.h.inc:1017
::mlir::Value getSelfValueFromConstrain()
Return the "self" value (i.e.
Definition Ops.cpp:512
::mlir::Value getSelfValueFromProduct()
Return the "self" value (i.e.
Definition Ops.cpp:507
void print(::mlir::OpAsmPrinter &p)
Definition Ops.cpp:193
bool nameIsCompute()
Return true iff the function name is FUNC_NAME_COMPUTE (if needed, a check that this FuncDefOp is loc...
Definition Ops.h.inc:1082
bool hasAllowWitnessAttr()
Return true iff the function def has the allow_witness attribute.
Definition Ops.h.inc:1009
::llzk::component::StructType getSingleResultTypeOfCompute()
Assuming the name is FUNC_NAME_COMPUTE, return the single StructType result.
Definition Ops.cpp:517
::mlir::StringAttr getResAttrsAttrName()
Definition Ops.h.inc:831
::mlir::ParseResult parse(::mlir::OpAsmParser &parser, ::mlir::OperationState &result)
Definition Ops.cpp:182
bool hasArgName(unsigned index)
Return true iff the argument at the given index has a function.arg_name attribute.
Definition Ops.cpp:316
void cloneInto(FuncDefOp dest, ::mlir::IRMapping &mapper)
Clone the internal blocks and attributes from this function into dest.
Definition Ops.cpp:202
void setAllowVerifOpsAttr(bool newValue=true)
Add (resp. remove) the allow_verif_ops attribute to (resp. from) the function def.
Definition Ops.cpp:298
bool hasResName(unsigned index)
Return true iff the result at the given index has a function.res_name attribute.
Definition Ops.cpp:331
bool nameIsProduct()
Return true iff the function name is FUNC_NAME_PRODUCT (if needed, a check that this FuncDefOp is loc...
Definition Ops.h.inc:1090
::std::optional<::mlir::StringAttr > getArgNameAttr(unsigned index)
Return the function.arg_name attribute for the argument at the given index.
Definition Ops.cpp:318
::std::optional<::mlir::StringAttr > getResNameAttr(unsigned index)
Return the function.res_name attribute for the result at the given index.
Definition Ops.cpp:333
bool nameIsConstrain()
Return true iff the function name is FUNC_NAME_CONSTRAIN (if needed, a check that this FuncDefOp is l...
Definition Ops.h.inc:1086
bool isStructCompute()
Return true iff the function is within a StructDefOp and named FUNC_NAME_COMPUTE.
Definition Ops.h.inc:1096
void setResName(unsigned index, ::llvm::StringRef name)
Set the function.res_name attribute for the result at the given index from a string.
Definition Ops.cpp:342
bool isInStruct()
Return true iff the function is within a StructDefOp.
Definition Ops.h.inc:1093
::llvm::ArrayRef<::mlir::Type > getResultTypes()
Required by FunctionOpInterface.
Definition Ops.h.inc:1071
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:847
void setAllowConstraintAttr(bool newValue=true)
Add (resp. remove) the allow_constraint attribute to (resp. from) the function def.
Definition Ops.cpp:274
::mlir::SymbolRefAttr getFullyQualifiedName(bool requireParent=true)
Return the full name for this function from the root module, including all surrounding symbol table n...
Definition Ops.cpp:477
bool hasArgPublicAttr(unsigned index)
Return true iff the argument at the given index has pub attribute.
Definition Ops.cpp:306
::llvm::LogicalResult verify()
Definition Ops.cpp:346
::mlir::Region & getBody()
Definition Ops.h.inc:871
bool hasAllowConstraintAttr()
Return true iff the function def has the allow_constraint attribute.
Definition Ops.h.inc:1001
::mlir::StringAttr getArgAttrsAttrName()
Definition Ops.h.inc:815
LLZK function operations.
Definition Dialect.h.inc:13
::llvm::LogicalResult verify()
Definition Ops.cpp:526
static constexpr ::llvm::StringLiteral getOperationName()
Definition Ops.h.inc:1217
static constexpr ::llvm::StringLiteral name
Definition Types.h.inc:47
OpClass::Properties & buildInstantiationAttrs(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, mlir::ArrayRef< mlir::ValueRange > mapOperands, mlir::DenseI32ArrayAttr numDimsPerMap, int32_t firstSegmentSize=0)
Utility for build() functions that initializes the operandSegmentSizes, mapOpGroupSizes,...
LogicalResult verifyAffineMapInstantiations(OperandRangeRange mapOps, ArrayRef< int32_t > numDimsPerMap, ArrayRef< AffineMapAttr > mapAttrs, Operation *origin)
OpClass::Properties & buildInstantiationAttrsEmpty(mlir::OpBuilder &odsBuilder, mlir::OperationState &odsState, int32_t firstSegmentSize=0)
Utility for build() functions that initializes the operandSegmentSizes, mapOpGroupSizes,...
bool isInStruct(Operation *op)
Definition Ops.cpp:57
LogicalResult checkSelfType(SymbolTableCollection &tables, StructDefOp expectedStruct, Type actualType, Operation *origin, const char *aspect)
Verifies that the given actualType matches the StructDefOp given (i.e., for the "self" type parameter...
Definition Ops.cpp:121
constexpr char ARG_NAME_ATTR_NAME[]
Attribute name for source-level function argument names.
Definition Ops.h:36
constexpr char RES_NAME_ATTR_NAME[]
Attribute name for source-level function result names.
Definition Ops.h:39
FunctionKind fnNameToKind(mlir::StringRef name)
Given a function name, return the corresponding FunctionKind.
Definition Ops.cpp:52
FunctionKind
Kinds of functions in LLZK.
Definition Ops.h:42
@ StructConstrain
Function within a struct named FUNC_NAME_CONSTRAIN.
Definition Ops.h:46
@ StructProduct
Function within a struct named FUNC_NAME_PRODUCT.
Definition Ops.h:48
@ StructCompute
Function within a struct named FUNC_NAME_COMPUTE.
Definition Ops.h:44
@ Free
Function that is not within a struct.
Definition Ops.h:50
constexpr char FUNC_NAME_COMPUTE[]
Symbol name for the witness generation (and resp.
Definition Constants.h:16
mlir::SymbolRefAttr getPrefixAsSymbolRefAttr(mlir::SymbolRefAttr symbol)
Return SymbolRefAttr like the one given but with the leaf/final element removed.
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
constexpr char FUNC_NAME_CONSTRAIN[]
Definition Constants.h:17
TypeClass getIfSingleton(mlir::TypeRange types)
Definition TypeHelper.h:293
void ensure(bool condition, const llvm::Twine &errMsg)
FailureOr< ModuleOp > getRootModule(Operation *from)
llvm::function_ref< InFlightDiagnosticWrapper()> EmitErrorFn
Callback to produce an error diagnostic.
bool isNullOrEmpty(mlir::ArrayAttr a)
OpClass getParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass'.
Definition OpHelpers.h:53
constexpr char FUNC_NAME_PRODUCT[]
Definition Constants.h:18
constexpr T checkedCast(U u) noexcept
Definition Compare.h:94
mlir::FailureOr< SymbolLookupResult< T > > resolveCallable(mlir::SymbolTableCollection &symbolTable, mlir::CallOpInterface call)
Based on mlir::CallOpInterface::resolveCallable, but using LLZK lookup helpers.
LogicalResult verifyTypeResolution(SymbolTableCollection &tables, Operation *origin, Type ty)
TypeClass getAtIndex(mlir::TypeRange types, size_t index)
Definition TypeHelper.h:297
OwningEmitErrorFn getEmitOpErrFn(mlir::Operation *op)
mlir::FailureOr< SymbolLookupResultUntyped > lookupSymbolIn(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, Within &&lookupWithin, mlir::Operation *origin, bool reportMissing=true)
std::function< InFlightDiagnosticWrapper()> OwningEmitErrorFn
This type is required in cases like the functions below to take ownership of the lambda so it is not ...
mlir::SymbolRefAttr getFullyQualifiedName(mlir::SymbolOpInterface symbol, bool requireParent=true)
Return the full name for this symbol from the root module, including any surrounding symbol table nam...
bool typesUnify(Type lhs, Type rhs, ArrayRef< StringRef > rhsReversePrefix, UnificationMap *unifications)
OpClass delegate_to_build(mlir::Location location, Args &&...args)
bool hasAffineMapAttr(Type type)
mlir::LogicalResult checkValidType(EmitErrorFn emitError, mlir::Type type)
Definition TypeHelper.h:137
void addTemplateParams(mlir::OpBuilder &odsBuilder, typename OpClass::Properties &props, llvm::ArrayRef< mlir::Attribute > templateParams)