22#include <mlir/Dialect/Arith/IR/Arith.h>
23#include <mlir/IR/AttrTypeSubElements.h>
24#include <mlir/IR/Builders.h>
25#include <mlir/IR/Diagnostics.h>
26#include <mlir/Interfaces/ControlFlowInterfaces.h>
28#include <llvm/ADT/MapVector.h>
29#include <llvm/ADT/STLFunctionalExtras.h>
35#define GEN_PASS_DEF_TEMPLATEMONOMORPHIZATIONPASS
54FailureOr<ArrayAttr> normalizeTypedArguments(
55 Operation *
source, ArrayAttr arguments, Operation *site, StringRef kind =
"struct"
61 SmallVector<Attribute> normalized;
62 for (
auto [parameter, argument] :
64 Attribute value = argument;
65 if (std::optional<Type> expected = parameter.getTypeOpt()) {
66 if (
auto indexType = dyn_cast<IndexType>(*expected)) {
67 if (
auto felt = dyn_cast<FeltConstAttr>(argument)) {
68 auto reduced =
felt.getReducedValue();
70 return site->emitError(
"field element ")
71 << argument <<
" cannot be converted to index parameter @"
72 << parameter.getSymName() <<
": a known field modulus is required";
74 const llvm::DynamicAPInt &number = *reduced;
75 if (number < 0 || number > std::numeric_limits<int64_t>::max()) {
76 return site->emitError(
"field element ")
77 << argument <<
" cannot be converted to index parameter @"
78 << parameter.getSymName()
79 <<
": value exceeds the nonnegative signed 64-bit index range";
81 value = IntegerAttr::get(indexType,
static_cast<int64_t
>(number));
83 auto integer = dyn_cast<IntegerAttr>(argument);
84 integer && integer.getType().isSignlessInteger(1)
86 value = IntegerAttr::get(indexType, integer.getValue().isOne() ? 1 : 0);
89 auto integerType = dyn_cast<IntegerType>(*expected);
90 integerType && integerType.isSignlessInteger(1)
92 if (
auto integer = dyn_cast<IntegerAttr>(argument);
93 integer && isa<IndexType>(integer.getType())) {
94 const APInt &number = integer.getValue();
95 if (!number.isZero() && !number.isOne()) {
96 return site->emitError(
"index argument ")
97 << argument <<
" cannot be converted to i1 parameter @" << parameter.getSymName()
98 <<
": expected zero or one";
100 value = IntegerAttr::get(integerType, number.isOne() ? 1 : 0);
102 }
else if (
auto feltType = dyn_cast<FeltType>(*expected)) {
103 if (
auto integer = dyn_cast<IntegerAttr>(argument);
104 integer && isa<IndexType>(integer.getType())) {
105 if (feltType.hasField()) {
107 auto reduced = field.
reduce(llvm::DynamicAPInt(integer.getValue()));
108 value = FeltConstAttr::get(
source->getContext(), reduced, feltType);
110 if (integer.getValue().isNegative()) {
111 return site->emitError(
"index argument ")
112 << argument <<
" cannot be converted to felt parameter @"
113 << parameter.getSymName() <<
": negative values require a known field modulus";
115 value = FeltConstAttr::get(
116 source->getContext(), llvm::DynamicAPInt(integer.getValue()), feltType
120 auto felt = dyn_cast<FeltConstAttr>(argument);
121 felt && (
felt.getType() == feltType || !
felt.getType().getFieldName())
125 llvm::DynamicAPInt number =
felt.getReducedValueOrRaw();
126 if (feltType.hasField()) {
127 number = feltType.getField().reduce(number);
129 value = FeltConstAttr::get(
source->getContext(), number, feltType);
132 bool matches =
false;
133 if (isa<TypeVarType>(*expected)) {
134 matches = isa<TypeAttr>(value);
135 }
else if (
auto typed = dyn_cast<TypedAttr>(value)) {
136 matches = typed.getType() == *expected;
139 return site->emitError() << kind <<
" argument " << argument
140 <<
" does not match parameter @" << parameter.getSymName()
141 <<
" of type " << *expected;
144 normalized.push_back(value);
146 return ArrayAttr::get(
source->getContext(), normalized);
151class TemplateInstantiationWorklist {
156 SmallVector<Operation *> visited;
159 struct FunctionEntry {
160 function::FuncDefOp source;
161 function::FuncDefOp clone;
162 SmallVector<Operation *> visited;
169 std::optional<Location> request;
172 using SpecializationKey = std::pair<Operation *, ArrayAttr>;
173 enum class KeyMode : std::uint8_t { Required, Optional };
178 SymbolTableCollection tables;
179 SmallVector<StructEntry> structEntries;
180 SmallVector<FunctionEntry> functionEntries;
181 DenseMap<SpecializationKey, unsigned> structCache;
182 DenseMap<SpecializationKey, unsigned> functionCache;
183 DenseMap<Operation *, unsigned> structCloneToId;
184 DenseMap<Operation *, unsigned> functionCloneToId;
188 FailureOr<ArrayAttr> canonicalizeCloneArguments(ArrayAttr arguments, Operation *site) {
189 AttrTypeReplacer replacer;
190 replacer.addReplacement(
191 [
this, site](StructType type) -> std::optional<std::pair<Type, WalkResult>> {
194 site->emitError(
"cannot resolve struct type argument ") << type;
195 return std::make_pair(Type(type), WalkResult::interrupt());
197 if (!structCloneToId.contains(found->get().getOperation())) {
202 if (!origin || !parameters) {
203 site->emitError(
"invalid struct specialization metadata for type argument ") << type;
204 return std::make_pair(Type(type), WalkResult::interrupt());
209 auto result = dyn_cast_if_present<ArrayAttr>(replacer.replace(arguments));
218 FailureOr<std::optional<SpecializationKey>>
219 getSpecializationKey(StructType type, Operation *lookupFrom, Operation *site, KeyMode mode) {
224 if (found->viaInclude()) {
225 return site->emitError(
"inline includes before template monomorphization");
229 arguments = ArrayAttr::get(root.getContext(), {});
231 if (!llvm::all_of(arguments, [](Attribute argument) {
234 if (mode == KeyMode::Optional) {
235 return std::optional<SpecializationKey>();
237 return site->emitError(
"template monomorphization requires concrete struct arguments")
241 if (failed(canonical)) {
244 auto sourceArguments = canonicalizeCloneArguments(*canonical, site);
245 if (failed(sourceArguments)) {
248 auto names = found->get().getType().getParams();
249 if ((
names ?
names.size() : 0) != sourceArguments->size()) {
250 if (mode == KeyMode::Optional) {
251 return std::optional<SpecializationKey>();
253 return site->emitError(
"struct specialization argument count mismatch");
255 auto normalized = normalizeTypedArguments(found->get().getOperation(), *sourceArguments, site);
256 if (failed(normalized)) {
259 return std::make_optional<SpecializationKey>(found->get().getOperation(), *normalized);
264 LogicalResult loadExistingSpecializations() {
265 auto existing = walkCollect<StructDefOp>(root, [](StructDefOp structure) {
268 for (
auto structure : existing) {
270 return structure.emitError(
"expected integer specialization identity");
273 llvm::sort(existing, [](StructDefOp left, StructDefOp right) {
277 for (
auto clone : existing) {
280 if (!origin || !arguments ||
283 return clone.emitError(
"invalid struct specialization metadata");
285 auto key = getSpecializationKey(
288 if (failed(key) || !*key) {
292 if (!structCache.try_emplace(**key, structEntries.size()).second) {
293 return clone.emitError(
"duplicate struct specialization identity");
295 structCloneToId[clone.getOperation()] = structEntries.size();
296 structEntries.push_back({cast<StructDefOp>((*key)->first), clone, {}});
302 LogicalResult loadExistingFunctions() {
303 auto existing = walkCollect<function::FuncDefOp>(root, [](function::FuncDefOp function) {
306 for (
auto clone : existing) {
309 if (!origin || !arguments) {
310 return clone.emitError(
"invalid function specialization metadata");
312 auto source = tables.lookupSymbolIn<function::FuncDefOp>(topRoot, origin);
314 return clone.emitError(
"cannot resolve function specialization origin");
316 auto templ =
source->getParentOfType<TemplateOp>();
317 if (arguments.size() !=
318 (templ ? llvm::range_size(templ.getConstOps<TemplateParamOp>()) : 0) ||
319 !llvm::all_of(arguments, [](Attribute argument) {
320 return isConcreteStructParamAttr(argument);
322 return clone.emitError(
"invalid function specialization arguments");
324 auto key = SpecializationKey(
source.getOperation(), arguments);
325 if (!functionCache.try_emplace(key, functionEntries.size()).second) {
326 return clone.emitError(
"duplicate function specialization identity");
328 functionCloneToId[clone.getOperation()] = functionEntries.size();
329 functionEntries.push_back({
source, clone, {},
false,
false, std::nullopt});
335 FailureOr<SpecializationKey> getFunctionKey(
336 function::CallOp call, function::FuncDefOp
source, ArrayRef<StringRef> targetNamespace
339 SmallVector<Attribute> arguments;
343 if (explicitArguments && explicitArguments.size() != llvm::range_size(parameters)) {
344 return call.emitError(
"function specialization argument count mismatch");
346 std::optional<UnificationMap> unified;
347 bool needsInference =
348 !explicitArguments || llvm::any_of(explicitArguments, [](Attribute arg) {
351 if (needsInference) {
353 call.unifyTypeSignatureWithNamespace(
source.getFunctionType(), targetNamespace);
354 if (failed(result)) {
355 return call.emitError(
"cannot infer concrete function specialization");
359 for (
auto [index, parameter] : llvm::enumerate(parameters)) {
360 Attribute value = explicitArguments ? explicitArguments[index] : Attribute();
362 value =
inferUnifiedParam(*unified, FlatSymbolRefAttr::get(parameter.getSymNameAttr()))
363 .value_or(Attribute());
366 return call.emitError(
"cannot resolve free-function template parameter @")
367 << parameter.getSymName();
369 if (failed(call.verifyTemplateParamValueCompatibility(value, parameter))) {
372 arguments.push_back(value);
376 tables, ArrayAttr::get(root.getContext(), arguments), call, topRoot, call
378 if (failed(canonical)) {
381 auto sourceArguments = canonicalizeCloneArguments(*canonical, call);
382 if (failed(sourceArguments)) {
385 auto normalized = normalizeTypedArguments(
source, *sourceArguments, call,
"function");
386 if (failed(normalized)) {
389 return SpecializationKey(
source.getOperation(), *normalized);
398 SmallVector<Attribute> values;
399 for (Attribute parameter : parameters) {
400 values.push_back(converter.convertAttr(parameter));
409 FailureOr<StructSpecializationDiscovery::Bindings>
410 bindArguments(Operation *
source, ArrayRef<Attribute>
names, ArrayAttr arguments) {
412 for (
auto [name, argument] : llvm::zip_equal(
names, arguments)) {
413 bindings[name] = argument;
423 static LogicalResult diagnoseUnresolvedBinding(
424 Operation *clone,
TemplateOp templ, Operation *site,
425 llvm::function_ref<
void(InFlightDiagnostic &)> describe
428 clone->walk([templ, site, describe](
ConstReadOp read)
mutable -> WalkResult {
430 auto diagnostic = site->emitError(
"template expression ");
432 describe(diagnostic);
434 auto diagnostic = site->emitError(
"unresolved template binding ");
436 describe(diagnostic);
438 return WalkResult::interrupt();
440 return failure(result.wasInterrupted());
445 static LogicalResult verifyStructuredControlFlow(Operation *
source) {
446 WalkResult result =
source->walk([](BranchOpInterface branch) -> WalkResult {
447 return branch->emitError(
448 "unstructured control flow is unsupported in template monomorphization"
451 return failure(result.wasInterrupted());
456 FailureOr<bool> hasSpecializedSignature(function::CallOp call) {
457 bool required =
false;
458 bool invalid =
false;
459 auto inspect = [
this, call, &required, &invalid](Type type) {
460 type.walk([
this, call, &required, &invalid](StructType structure) {
461 auto key = getSpecializationKey(structure, call, call, KeyMode::Optional);
464 return WalkResult::interrupt();
466 if (*key && structCache.contains(**key)) {
468 return WalkResult::interrupt();
470 return WalkResult::advance();
473 for (Type type : call.getResultTypes()) {
486 LogicalResult instantiateFunction(function::CallOp call,
bool active =
true) {
488 if (failed(target)) {
491 if (target->viaInclude()) {
492 return call.emitError(
"inline includes before template monomorphization");
494 auto source = target->get();
495 if (
auto clone = functionCloneToId.find(
source.getOperation());
496 clone != functionCloneToId.end()) {
497 functionEntries[clone->second].active |= active;
500 if (
source.isExternal()) {
503 auto key = getFunctionKey(call,
source, target->getNamespace());
508 if (
auto found = functionCache.find(*key); found != functionCache.end()) {
510 functionEntries[id].active |= active;
512 if (structEntries.size() + functionEntries.size() >= limit) {
513 return call.emitError(
"template monomorphization specialization limit exceeded");
515 if (failed(verifyStructuredControlFlow(
source))) {
519 if (failed(destination)) {
523 if (failed(localArguments)) {
527 return call->emitError(
"free-function specialization origin");
529 if (failed(origin)) {
532 auto templ =
source->getParentOfType<TemplateOp>();
533 SmallVector<Attribute>
names;
535 for (
auto parameter : templ.
getConstOps<TemplateParamOp>()) {
536 names.push_back(FlatSymbolRefAttr::get(parameter.getSymNameAttr()));
539 auto evaluated = bindArguments(
source,
names, *localArguments);
540 if (failed(evaluated)) {
543 auto bindings = std::move(*evaluated);
544 auto clone =
source.clone();
545 clone.setSymName((
source.getSymName() +
"__spec_" + Twine(functionEntries.size())).str());
546 Operation *parent = templ ? templ->getParentOp() :
source->getParentOp();
547 tables.getSymbolTable(parent).insert(clone);
548 id = functionEntries.size();
549 functionCache[*key] = id;
550 functionCloneToId[clone.getOperation()] = id;
551 functionEntries.push_back({
source, clone, {}, active,
false, call.getLoc()});
553 convertCallArguments(clone, bindings);
554 SmallVector<Diagnostic> diagnostics;
558 if (failed(diagnoseUnresolvedBinding(
560 [name = *origin, arguments = key->second](InFlightDiagnostic &diagnostic) {
561 diagnostic <<
"free function " << name <<
" with arguments " << arguments;
571 if (failed(lookupRoot)) {
575 return call->emitError(
"specialized free-function callee");
587 FailureOr<unsigned> instantiate(StructType type, Operation *site) {
588 auto resolved = getSpecializationKey(type, site, site, KeyMode::Required);
589 if (failed(resolved) || !*resolved) {
592 auto key = **resolved;
593 StructDefOp
source = cast<StructDefOp>(key.first);
594 if (
auto existing = structCloneToId.find(
source); existing != structCloneToId.end()) {
595 return existing->second;
598 if (failed(destination)) {
603 if (failed(localArguments)) {
606 if (
auto existing = structCache.find(key); existing != structCache.end()) {
607 return existing->second;
609 if (structEntries.size() + functionEntries.size() >= limit) {
610 return site->emitError(
"template monomorphization specialization limit exceeded");
612 if (failed(verifyStructuredControlFlow(
source))) {
615 auto templ =
source->getParentOfType<TemplateOp>();
617 bindArguments(
source,
names ?
names.getValue() : ArrayRef<Attribute>(), *localArguments);
618 if (failed(evaluated)) {
621 auto bindings = std::move(*evaluated);
622 TemplateTypeConverter parameterConverter(bindings);
623 auto reads =
source.walk([&bindings, ¶meterConverter](ConstReadOp read) -> WalkResult {
626 parameterConverter.convertType(read.getType())
629 if (reads.wasInterrupted()) {
632 auto clone =
source.clone();
633 clone.setSymName((
source.getSymName() +
"__spec_" + Twine(structEntries.size())).str());
634 Operation *parent = templ ? templ->getParentOp() :
source->getParentOp();
635 tables.getSymbolTable(parent).insert(clone);
636 unsigned id = structEntries.size();
637 structCache[key] = id;
638 structCloneToId[clone.getOperation()] = id;
639 structEntries.push_back({
source, clone, {}});
641 clone.walk([¶meterConverter](function::CallOp call) {
643 SmallVector<Attribute> values;
644 for (Attribute parameter : parameters) {
645 values.push_back(parameterConverter.convertAttr(parameter));
651 StructType canonicalSelf =
source.getType();
652 AttrTypeReplacer selfTypes;
653 selfTypes.addReplacement(
654 [canonicalSelf](StructType current) -> std::optional<std::pair<Type, WalkResult>> {
659 return std::make_pair(Type(canonicalSelf), WalkResult::skip());
662 selfTypes.recursivelyReplaceElementsIn(clone,
true,
false,
true);
665 auto constants = clone.walk([&bindings, ¶meterConverter](ConstReadOp read) -> WalkResult {
668 parameterConverter.convertType(read.getType())
671 return WalkResult::interrupt();
674 return WalkResult::advance();
676 OpBuilder builder(read);
678 if (
auto felt = dyn_cast<FeltConstAttr>(*value)) {
681 constant = arith::ConstantOp::create(builder, read.getLoc(), cast<TypedAttr>(*value));
683 read.replaceAllUsesWith(constant);
685 return WalkResult::advance();
687 if (constants.wasInterrupted()) {
690 SmallVector<Diagnostic> diagnostics;
694 if (failed(diagnoseUnresolvedBinding(
695 clone, templ, site, [type](InFlightDiagnostic &diagnostic) { diagnostic << type; }
700 Builder builder(root.getContext());
703 return source->emitError(
"specialization origin");
705 if (failed(origin)) {
715 void recordFamily(
const StructSpecializationRequest &request,
unsigned id) {
716 Builder builder(root.getContext());
719 if (request.
kind == StructSpecializationRequest::Kind::ArrayElement) {
721 SmallVector<Attribute> indices;
723 indices.push_back(builder.getIndexAttr(index));
725 record = builder.getDictionaryAttr(
730 }
else if (request.
kind == StructSpecializationRequest::Kind::RolledCallResult) {
732 record = builder.getI64IntegerAttr(
id);
736 SmallVector<Attribute> records;
737 if (
auto existing = request.
site->getAttrOfType<ArrayAttr>(name)) {
738 llvm::append_range(records, existing);
740 if (!llvm::is_contained(records, record)) {
741 records.push_back(record);
743 request.
site->setAttr(name, builder.getArrayAttr(records));
748 LogicalResult recordRolledCallTargets(
const RolledCallTargets &targets) {
749 auto call = cast<function::CallOp>(targets.
site);
750 SmallVector<Attribute> ids;
752 llvm::append_range(ids, existing);
754 for (StructType candidate : targets.
candidates) {
755 auto id = instantiate(candidate, call);
759 auto attr = Builder(root.getContext()).getI64IntegerAttr(*
id);
760 if (!llvm::is_contained(ids, attr)) {
770 LogicalResult setSpecializedCallee(function::CallOp call, StructDefOp clone, StringAttr method) {
772 if (failed(lookupRoot)) {
776 return call->emitError(
"specialized method owner");
787 LogicalResult retargetCall(function::CallOp call,
const StructEntry *caller,
bool visited) {
789 if (failed(target)) {
792 if (target->viaInclude()) {
793 return call.emitError(
"inline includes before template monomorphization");
795 auto owner = target->get()->getParentOfType<StructDefOp>();
802 SmallVector<Type> types(call.getResultTypes());
804 for (Type type : types) {
805 auto structure = dyn_cast<StructType>(type);
813 auto cloneId = structCloneToId.find(found->get().getOperation());
814 if (cloneId != structCloneToId.end() && structEntries[cloneId->second].source == owner) {
815 return setSpecializedCallee(
816 call, structEntries[cloneId->second].clone, target->get().getSymNameAttr()
819 if (found->get() == owner) {
823 if (caller && owner == caller->source) {
824 return setSpecializedCallee(call, caller->clone, target->get().getSymNameAttr());
827 return call.emitError(
"cannot determine concrete struct owner for method call");
834 LogicalResult verifyExternalCalls() {
835 auto check = [
this](Operation *clone) {
836 return clone->walk([
this](function::CallOp call) {
838 if (failed(target)) {
839 return WalkResult::interrupt();
841 if (target->get()->getParentOfType<StructDefOp>() || !target->get().isExternal()) {
842 return WalkResult::advance();
844 auto required = hasSpecializedSignature(call);
845 if (failed(required)) {
846 return WalkResult::interrupt();
849 call.emitError(
"external function ")
851 <<
" cannot be called with specialized struct types; external declarations are "
853 return WalkResult::interrupt();
855 return WalkResult::advance();
858 for (StructEntry &entry : structEntries) {
859 if (
check(entry.clone).wasInterrupted()) {
863 for (FunctionEntry &entry : functionEntries) {
864 if (
check(entry.clone).wasInterrupted()) {
872 TemplateInstantiationWorklist(ModuleOp module,
unsigned maximum) : root(module), limit(maximum) {}
876 LogicalResult run() {
882 if (failed(loadExistingSpecializations())) {
885 if (failed(loadExistingFunctions())) {
890 return root.emitError(
"template monomorphization requires a concrete llzk.main");
892 auto mainId = instantiate(*
main, root);
893 if (failed(mainId)) {
897 for (
unsigned structIndex = 0;;) {
898 bool isStruct = structIndex < structEntries.size();
899 unsigned functionIndex = 0;
902 functionIndex < functionEntries.size() &&
903 (!functionEntries[functionIndex].active || functionEntries[functionIndex].discovered)) {
906 if (functionIndex == functionEntries.size()) {
910 Operation *clone = isStruct ? structEntries[structIndex].clone.getOperation()
911 : functionEntries[functionIndex].clone.getOperation();
912 SmallVector<Operation *> visited;
913 std::optional<ScopedDiagnosticHandler> requestNote;
914 if (!isStruct && functionEntries[functionIndex].request) {
915 Location request = *functionEntries[functionIndex].request;
918 requestNote.emplace(clone->getContext(), [request, origin, arguments](Diagnostic &diag) {
919 if (diag.getSeverity() == DiagnosticSeverity::Error) {
920 diag.attachNote(request)
921 <<
"while specializing free function " << origin <<
" with arguments " << arguments;
927 StructSpecializationDiscovery discovery;
931 if (failed(requests)) {
934 DenseSet<Operation *> visitedSet(visited.begin(), visited.end());
935 for (
const auto &request : *requests) {
936 auto id = instantiate(request.type, request.site);
940 recordFamily(request, *
id);
942 for (
const auto &targets : rolledCalls) {
943 if (failed(recordRolledCallTargets(targets))) {
947 auto calls = clone->walk([
this, &visitedSet](function::CallOp call) {
948 if (!visitedSet.contains(call)) {
949 return WalkResult::advance();
952 if (failed(target)) {
953 return WalkResult::interrupt();
955 if (!target->get()->getParentOfType<StructDefOp>() && failed(instantiateFunction(call))) {
956 return WalkResult::interrupt();
958 return WalkResult::advance();
960 if (calls.wasInterrupted()) {
964 structEntries[structIndex].visited = std::move(visited);
967 functionEntries[functionIndex].visited = std::move(visited);
968 functionEntries[functionIndex].discovered =
true;
974 for (
unsigned structIndex = 0, functionIndex = 0;
975 structIndex < structEntries.size() || functionIndex < functionEntries.size();) {
976 bool isStruct = structIndex < structEntries.size();
977 Operation *clone = isStruct ? structEntries[structIndex].clone.getOperation()
978 : functionEntries[functionIndex].clone.getOperation();
980 isStruct ? structEntries[structIndex].visited : functionEntries[functionIndex].visited;
981 DenseSet<Operation *> visitedSet(visited.begin(), visited.end());
982 auto calls = clone->walk([
this, &visitedSet](function::CallOp call) {
983 if (visitedSet.contains(call)) {
984 return WalkResult::advance();
987 if (failed(target)) {
988 return WalkResult::interrupt();
990 if (target->get()->getParentOfType<StructDefOp>()) {
991 return WalkResult::advance();
993 auto required = hasSpecializedSignature(call);
994 if (failed(required) || (*required && failed(instantiateFunction(call,
false)))) {
995 return WalkResult::interrupt();
997 return WalkResult::advance();
999 if (calls.wasInterrupted()) {
1008 if (failed(verifyExternalCalls())) {
1014 llvm::MapVector<Operation *, SmallVector<Operation *>> clonesByRoot;
1015 for (StructEntry &entry : structEntries) {
1017 if (failed(lookupRoot)) {
1020 clonesByRoot[lookupRoot->getOperation()].push_back(entry.clone);
1022 for (FunctionEntry &entry : functionEntries) {
1024 if (failed(lookupRoot)) {
1027 clonesByRoot[lookupRoot->getOperation()].push_back(entry.clone);
1029 for (
auto &[rootOp, clones] : clonesByRoot) {
1030 auto lookupRoot = cast<ModuleOp>(rootOp);
1033 bool invalid =
false;
1037 AttrTypeReplacer replacer;
1038 replacer.addReplacement(
1040 &invalid](StructType type) -> std::optional<std::pair<Type, WalkResult>> {
1041 auto key = getSpecializationKey(type, lookupRoot, lookupRoot, KeyMode::Optional);
1044 return std::make_pair(Type(type), WalkResult::skip());
1047 return std::nullopt;
1049 auto existing = structCache.find(**key);
1050 if (existing == structCache.end()) {
1051 return std::nullopt;
1054 structEntries[existing->second].clone, lookupRoot,
1055 [lookupRoot] { return lookupRoot->emitError(
"specialized struct"); }
1059 return std::make_pair(Type(type), WalkResult::skip());
1061 return std::make_pair(Type(
StructType::get(*name)), WalkResult::skip());
1064 for (Operation *clone : clones) {
1068 replacer.recursivelyReplaceElementsIn(clone,
true,
false,
true);
1076 for (StructEntry &entry : structEntries) {
1077 DenseSet<Operation *> visited(entry.visited.begin(), entry.visited.end());
1078 auto result = entry.clone.walk([
this, &entry, &visited](function::CallOp call) {
1079 return WalkResult(retargetCall(call, &entry, visited.contains(call)));
1081 if (result.wasInterrupted()) {
1085 for (FunctionEntry &entry : functionEntries) {
1086 DenseSet<Operation *> visited(entry.visited.begin(), entry.visited.end());
1087 auto result = entry.clone.walk([
this, &visited](function::CallOp call) {
1088 return WalkResult(retargetCall(call,
nullptr, visited.contains(call)));
1090 if (result.wasInterrupted()) {
1095 if (failed(mainRoot)) {
1099 return root.emitError(
"specialized main");
1101 if (failed(mainName)) {
1110class TemplateMonomorphizationPass
1112 TemplateMonomorphizationPass> {
1114 using Base = TemplateMonomorphizationPassBase<TemplateMonomorphizationPass>;
1118 void runOnOperation()
override {
1119 if (failed(TemplateInstantiationWorklist(getOperation(), specializationLimit).run())) {
1120 signalPassFailure();
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
within a display generated by the Derivative if and wherever such third party notices normally appear The contents of the NOTICE file are for informational purposes only and do not modify the License You may add Your own attribution notices within Derivative Works that You alongside or as an addendum to the NOTICE text from the provided that such additional attribution notices cannot be construed as modifying the License You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for or distribution of Your or for any such Derivative Works as a provided Your and distribution of the Work otherwise complies with the conditions stated in this License Submission of Contributions Unless You explicitly state any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this without any additional terms or conditions Notwithstanding the nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions Trademarks This License does not grant permission to use the trade names
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for and distribution as defined by Sections through of this document Licensor shall mean the copyright owner or entity authorized by the copyright owner that is granting the License Legal Entity shall mean the union of the acting entity and all other entities that control are controlled by or are under common control with that entity For the purposes of this definition control direct or to cause the direction or management of such whether by contract or including but not limited to software source documentation source
Common private implementation for poly dialect passes.
Private parameter substitution helpers shared by polymorphic transformations.
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).
static const Field & getField(llvm::StringRef fieldName, EmitErrorFn errFn)
Get a Field from a given field name string.
::mlir::SymbolRefAttr getNameRef() const
static StructType get(::mlir::SymbolRefAttr structName)
::mlir::FailureOr< SymbolLookupResult< StructDefOp > > getDefinition(::mlir::SymbolTableCollection &symbolTable, ::mlir::Operation *op, bool reportMissing=true) const
Gets the struct op that defines this struct.
::mlir::ArrayAttr getParams() const
static FeltConstantOp create(::mlir::OpBuilder &builder, ::mlir::Location location, ::mlir::Type result, ::llzk::felt::FeltConstAttr value)
::mlir::SymbolRefAttr getCalleeAttr()
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
::mlir::Operation::operand_range getArgOperands()
::mlir::ArrayAttr getTemplateParamsAttr()
void setCalleeAttr(::mlir::SymbolRefAttr attr)
::mlir::Attribute removeTemplateParamsAttr()
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
::mlir::FlatSymbolRefAttr getConstNameAttr()
OpT getConstNamed(::mlir::StringRef find)
Return the op of type OpT with the given name within the body region if it exists,...
inline ::llvm::iterator_range<::mlir::Region::op_iterator< OpT > > getConstOps()
Return ops of type OpT within the body region.
Determine which concrete struct definitions are needed when instantiating a struct or free function w...
mlir::FailureOr< Requests > discover(mlir::Operation *source, const Bindings &bindings, llvm::SmallVectorImpl< mlir::Operation * > *visitedOperations=nullptr, CallTargets *callTargets=nullptr) const
Return ordered, deduplicated requests for a struct or free-function source, or diagnose an unresolved...
llvm::DenseMap< mlir::Attribute, mlir::Attribute > Bindings
mlir::FailureOr< Bindings > evaluateBindings(mlir::Operation *source, const Bindings &bindings) const
Evaluate enclosing template expressions in declaration order and return an extended copy of bindings ...
llvm::SmallVector< RolledCallTargets > CallTargets
Substitute known template bindings in function types and inference candidates.
int main(int argc, char **argv)
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
component::StructType getStructTypeWithParams(mlir::SymbolRefAttr nameRef, mlir::ArrayAttr params)
Build a struct type while representing an empty parameter list as absent.
FailureOr< ArrayAttr > rebaseTemplateParams(SymbolTableCollection &tables, ArrayAttr templateParams, Operation *lookupFrom, ModuleOp destinationRoot, Operation *requestSite)
std::optional< Attribute > inferUnifiedParam(const UnificationMap &unifyResult, SymbolRefAttr paramName)
Return the callee-side unification-derived value for a template parameter, if any.
LogicalResult substituteStructBody(StructDefOp newStruct, StructType typeAtDef, const DenseMap< Attribute, Attribute > ¶mNameToConcrete, SmallVector< Diagnostic > &diagnostics)
LogicalResult substituteFunctionBody(FuncDefOp newFunc, const DenseMap< Attribute, Attribute > ¶mNameToConcrete, SmallVector< Diagnostic > &delayedDiagnostics)
mlir::FailureOr< mlir::Attribute > resolveConstReadBinding(ConstReadOp read, mlir::Attribute binding, mlir::Type readType)
Materialize a binding with the resolved read type for evaluation or cloning.
void convertCalleesInPlace(Operation *op, const DenseMap< Attribute, Attribute > ¶mNameToValue)
void reportDelayedDiagnostics(Operation *site, SmallVector< Diagnostic > &&diagnostics)
constexpr llvm::StringLiteral FAMILY_ATTR
constexpr llvm::StringLiteral SPECIALIZATION_ID_ATTR
Metadata on concrete struct clones and rolled affine family uses.
constexpr llvm::StringLiteral SPECIALIZATION_ORIGIN_ATTR
constexpr llvm::StringLiteral SPECIALIZATION_ARGUMENTS_ATTR
constexpr llvm::StringLiteral FAMILY_SPECIALIZATION_KEY
constexpr llvm::StringLiteral FAMILY_INDICES_KEY
constexpr llvm::StringLiteral FAMILY_TYPE_KEY
The original affine-parameterized struct family, paired with element indices and a concrete specializ...
constexpr llvm::StringLiteral FAMILY_SPECIALIZATIONS_ATTR
FailureOr< StructType > getMainInstanceType(Operation *lookupFrom)
FailureOr< ModuleOp > getRootModule(Operation *from)
SymbolRefAttr appendLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf)
AttrConcreteness classifyAttrConcreteness(Attribute attr, bool allowStructParams)
constexpr T checkedCast(U u) noexcept
FailureOr< ModuleOp > getTopRootModule(Operation *from)
bool isConcreteStructParamAttr(mlir::Attribute attr, bool allowStructParams=true)
Return true if attr is a concrete argument for a parameterized struct type.
FailureOr< SymbolRefAttr > getPathRelativeToAncestor(SymbolOpInterface symbol, ModuleOp ancestor, llvm::function_ref< InFlightDiagnostic()> emitError)
bool hasAffineMapAttr(Type type)
constexpr char MAIN_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that specifies the type of the main struct.
llvm::SmallVector< component::StructType > candidates
llvm::SmallVector< int64_t > arrayIndices
component::StructType familyType