24#include <mlir/Dialect/SCF/IR/SCF.h>
25#include <mlir/IR/AttrTypeSubElements.h>
26#include <mlir/IR/SymbolTable.h>
27#include <mlir/Interfaces/SideEffectInterfaces.h>
29#include <llvm/ADT/DenseSet.h>
30#include <llvm/ADT/TypeSwitch.h>
49class SpecializationTypeResolver {
56 : bindings(parameters) {}
59 Attribute resolveAttribute(Attribute attr)
const {
60 if (
auto bound = bindings.lookup(attr)) {
63 if (
auto type = dyn_cast<TypeAttr>(attr)) {
64 return TypeAttr::get(resolveType(type.getValue()));
70 Type resolveType(Type type)
const {
71 AttrTypeReplacer replacer;
72 replacer.addReplacement([
this](TypeVarType variable) -> std::optional<Type> {
73 if (
auto bound = dyn_cast_or_null<TypeAttr>(bindings.lookup(variable.
getNameRef()))) {
74 return bound.getValue();
78 replacer.addReplacement(
79 [
this](StructType structure) -> std::optional<std::pair<Type, WalkResult>> {
80 SmallVector<Attribute> args;
82 return std::make_pair(Type(structure), WalkResult::skip());
84 for (Attribute arg : structure.
getParams()) {
85 args.push_back(resolveAttribute(arg));
87 return std::make_pair(
95 replacer.addReplacement(
96 [
this](array::ArrayType array) -> std::optional<std::pair<Type, WalkResult>> {
97 SmallVector<Attribute> dimensions;
99 dimensions.push_back(resolveAttribute(dimension));
101 return std::make_pair(
110 return replacer.replace(type);
114 Attribute resolveAffineArgument(AffineMapAttr attr, ArrayRef<Attribute> operands)
const {
115 if (attr.getValue().getNumInputs() != operands.size() ||
116 llvm::any_of(operands, [](Attribute operand) {
117 return !llvm::isa_and_present<IntegerAttr>(operand);
121 SmallVector<Attribute> results;
123 if (failed(attr.getValue().constantFold(operands, results, &poison)) || poison ||
124 results.size() != 1) {
127 return results.front();
132 template <
typename ResolveArgument>
133 FailureOr<StructType>
134 resolveAffineStructArguments(StructType structure, ResolveArgument &&resolveArgument)
const {
135 SmallVector<Attribute> arguments;
136 for (Attribute argument : structure.
getParams()) {
137 if (
auto map = dyn_cast<AffineMapAttr>(argument)) {
138 argument = resolveArgument(map);
143 arguments.push_back(argument);
146 structure.
getNameRef(), ArrayAttr::get(structure.getContext(), arguments)
166 SpecializationTypeResolver typeResolver;
167 SymbolTableCollection tables;
168 DiscoveryStorage storage {source->getContext()};
170 DenseSet<std::tuple<Operation *, Type, ArrayAttr, Type, unsigned>> seen;
171 DenseSet<std::pair<Operation *, Type>> enumeratedArrays;
172 using FamilyKey = std::pair<Operation *, ArrayAttr>;
173 DenseMap<FamilyKey, SmallVector<StructType>> familyCandidates;
174 DenseSet<FamilyKey> knownFamilies;
175 SmallVector<std::pair<function::CallOp, StructType>> rolledCalls;
176 DenseSet<std::pair<Operation *, Type>> seenRolledCalls;
177 SmallVectorImpl<Operation *> *visitedOperations;
181 LogicalResult recordRequest(
182 Operation *site, StructType type, ArrayRef<int64_t> indices = {}, StructType familyType = {},
186 for (Attribute argument : type.
getParams()) {
187 if (
auto nested = dyn_cast<TypeAttr>(argument)) {
189 return site->emitError(
"affine maps nested in struct type arguments are not supported");
193 return site->emitError(
"cannot resolve struct specialization argument ") << argument;
195 if (
auto nested = dyn_cast<TypeAttr>(argument)) {
196 if (failed(collectTypes(site, nested.getValue()))) {
202 if (
auto structure = dyn_cast<StructDefOp>(source);
204 type == llvm::cast<StructType>(typeResolver.resolveType(structure.getType()))) {
207 SmallVector<Attribute> indexAttrs;
208 for (int64_t index : indices) {
209 indexAttrs.push_back(IntegerAttr::get(IndexType::get(context()), index));
211 auto requestKey = std::make_tuple(
212 site, Type(type), ArrayAttr::get(context(), indexAttrs), Type(familyType),
213 static_cast<unsigned>(kind)
215 if (!seen.insert(requestKey).second) {
218 requests.push_back({site, type, llvm::to_vector(indices), familyType, kind});
222 static bool hasDirectAffineArgument(StructType type) {
224 return isa<AffineMapAttr>(argument);
228 FailureOr<FamilyKey> getFamilyKey(Operation *site, StructType rolled) {
233 return FamilyKey(found->get().getOperation(), rolled.
getParams());
236 LogicalResult rememberFamily(Operation *site, StructType rolled) {
237 auto key = getFamilyKey(site, rolled);
241 knownFamilies.insert(*key);
245 LogicalResult recordFamilyCandidate(Operation *site, StructType rolled, StructType concrete) {
246 auto key = getFamilyKey(site, rolled);
250 knownFamilies.insert(*key);
251 auto &candidates = familyCandidates[*key];
252 if (!llvm::is_contained(candidates, concrete)) {
253 candidates.push_back(concrete);
261 collectTypes(Operation *site, Type type, ArrayRef<int64_t> indices = {},
bool inArray =
false) {
262 type = typeResolver.resolveType(type);
263 if (
auto structure = dyn_cast<StructType>(type)) {
264 if (!hasDirectAffineArgument(structure)) {
265 return recordRequest(site, structure);
268 if (isa<MemberDefOp>(site)) {
269 return site->emitError(
"affine struct type requires an enclosing array");
273 SmallVector<Attribute> operands;
274 for (int64_t index : indices) {
275 operands.push_back(IntegerAttr::get(IndexType::get(context()), index));
277 auto concrete = typeResolver.resolveAffineStructArguments(
278 structure, [
this, site, &operands](AffineMapAttr map) -> Attribute {
279 if (map.getValue().getNumInputs() != operands.size()) {
280 site->emitError(
"affine struct argument requires one input per array dimension");
283 Attribute argument = typeResolver.resolveAffineArgument(map, operands);
285 site->emitError(
"cannot resolve affine array element specialization argument");
290 if (failed(concrete)) {
293 if (failed(recordFamilyCandidate(site, structure, *concrete))) {
296 return recordRequest(
297 site, *concrete, indices, structure, StructSpecializationRequest::Kind::ArrayElement
300 if (
auto array = dyn_cast<array::ArrayType>(type)) {
304 if (!enumeratedArrays.insert({site, type}).second) {
307 SmallVector<int64_t> shape;
309 auto integer = dyn_cast<IntegerAttr>(dimension);
310 if (!integer || integer.getInt() < 0) {
311 return site->emitError(
"affine family discovery requires concrete array dimensions");
313 shape.push_back(integer.getInt());
315 if (llvm::is_contained(shape, 0)) {
316 bool invalid =
false;
318 if (
auto structure = dyn_cast<StructType>(nested);
319 structure && hasDirectAffineArgument(structure)) {
320 invalid |= failed(rememberFamily(site, structure));
323 return failure(invalid);
325 SmallVector<int64_t> elementIndices;
326 auto enumerate = [
this, site, array, &shape,
327 &elementIndices](
auto &&self,
unsigned dimension) -> LogicalResult {
328 if (dimension < shape.size()) {
329 for (int64_t i = 0; i < shape[dimension]; ++i) {
330 elementIndices.push_back(i);
331 if (failed(self(self, dimension + 1))) {
334 elementIndices.pop_back();
338 return collectTypes(site, array.getElementType(), elementIndices,
true);
340 return enumerate(enumerate, 0);
342 if (
auto record = dyn_cast<pod::PodType>(type)) {
343 for (
auto field : record.getRecords()) {
344 if (failed(collectTypes(site, field.getType(), indices, inArray))) {
350 return failure(type.walk([
this, site, indices, inArray](StructType structure) -> WalkResult {
351 return collectTypes(site, structure, indices, inArray);
352 }).wasInterrupted());
356 LogicalResult discoverCallTypes(function::CallOp call,
const Environment &env) {
358 for (Type result : call.getResultTypes()) {
359 auto structure = dyn_cast<StructType>(typeResolver.resolveType(result));
360 if (!structure || !structure.
getParams()) {
361 if (failed(collectTypes(call, result))) {
366 auto concrete = typeResolver.resolveAffineStructArguments(
367 structure, [
this, &call, &env, &group](AffineMapAttr map) -> Attribute {
369 call.emitError(
"missing affine specialization operands");
372 SmallVector<Attribute> operands;
374 operands.push_back(env.lookup(operand).constant);
376 Attribute argument = typeResolver.resolveAffineArgument(map, operands);
379 "monomorphize-templates: cannot resolve affine struct specialization argument"
385 if (failed(concrete)) {
388 bool rolledResult = hasDirectAffineArgument(structure);
390 if (failed(recordFamilyCandidate(call, structure, *concrete))) {
394 auto kind = rolledResult ? StructSpecializationRequest::Kind::RolledCallResult
395 : StructSpecializationRequest::Kind::Plain;
396 if (failed(recordRequest(call, *concrete, {}, {}, kind))) {
401 if (
auto structure = dyn_cast<StructType>(typeResolver.resolveType(argument));
402 structure && hasDirectAffineArgument(structure)) {
403 if (seenRolledCalls.insert({call, structure}).second) {
404 rolledCalls.emplace_back(call, structure);
412 static std::optional<int64_t> getKnownInteger(
const DiscoveryValue &fact) {
413 auto value = dyn_cast_or_null<IntegerAttr>(fact.
constant);
414 if (!value || value.getValue().getSignificantBits() > 64) {
417 return value.getInt();
421 static void bindResults(ValueRange results, ArrayRef<DiscoveryValue> values,
Environment &env) {
422 for (
auto [result, value] : llvm::zip_equal(results, values)) {
430 evaluateIf(scf::IfOp branch,
const Environment &env, SmallVector<DiscoveryValue> &results) {
431 auto condition = getKnownInteger(env.lookup(branch.getCondition()));
432 SmallVector<DiscoveryValue> left, right;
434 storage.invalidate();
436 if (!condition || *condition) {
437 if (failed(evaluateBlock(branch.getThenRegion().front(), env, left))) {
442 storage.invalidate();
444 if ((!condition || !*condition) && !branch.getElseRegion().empty()) {
445 if (failed(evaluateBlock(branch.getElseRegion().front(), env, right))) {
450 storage.invalidate();
453 results = *condition ? left : right;
455 for (
unsigned i = 0; i < results.size(); ++i) {
456 results[i] = left[i] == right[i] ? left[i] : Attribute();
465 evaluateFor(scf::ForOp loop,
const Environment &env, SmallVector<DiscoveryValue> &results) {
466 auto lower = getKnownInteger(env.lookup(loop.getLowerBound()));
467 auto upper = getKnownInteger(env.lookup(loop.getUpperBound()));
468 auto stride = getKnownInteger(env.lookup(loop.getStep()));
469 if (!lower || !upper || !stride || *stride <= 0 || loop.getUnsignedCmp()) {
470 return loop.emitError(
471 "specialization discovery requires known signed 'for' bounds and a positive step"
475 for (Value initial : loop.getInitArgs()) {
476 results.push_back(env.lookup(initial));
478 for (int64_t i = *lower; i < *upper;) {
480 local[loop.getInductionVar()] = IntegerAttr::get(loop.getInductionVar().getType(), i);
481 bindResults(loop.getRegionIterArgs(), results, local);
483 if (failed(evaluateBlock(*loop.getBody(), std::move(local), results))) {
498 evaluateWhile(scf::WhileOp loop,
const Environment &env, SmallVector<DiscoveryValue> &results) {
500 return loop.emitError(
"specialization discovery cannot execute an always-true while loop");
502 SmallVector<DiscoveryValue> carried;
503 for (Value initial : loop.getInits()) {
504 carried.push_back(env.lookup(initial));
508 bindResults(loop.getBefore().front().getArguments(), carried, before);
509 SmallVector<DiscoveryValue> forwarded;
512 evaluateBlock(loop.getBefore().front(), std::move(before), forwarded, &condition)
516 auto known = dyn_cast_or_null<IntegerAttr>(condition);
517 if (!known || !known.getType().isInteger(1)) {
521 storage.invalidate();
522 for (Region *region : {&loop.getBefore(), &loop.getAfter()}) {
524 for (BlockArgument argument : region->front().getArguments()) {
525 local[argument] = {};
527 SmallVector<DiscoveryValue> ignored;
528 Attribute ignoredCondition;
530 evaluateBlock(region->front(), std::move(local), ignored, &ignoredCondition)
535 storage.invalidate();
536 results.assign(loop.getNumResults(), DiscoveryValue());
539 if (known.getValue().isZero()) {
540 results = std::move(forwarded);
544 bindResults(loop.getAfter().front().getArguments(), forwarded, after);
546 if (failed(evaluateBlock(loop.getAfter().front(), std::move(after), carried))) {
554 FailureOr<Attribute> evaluateConstRead(ConstReadOp read) {
556 read, bindings.lookup(read.
getConstNameAttr()), typeResolver.resolveType(read.getType())
563 evaluateFoldableOp(Operation &op,
const Environment &env, SmallVector<DiscoveryValue> &results) {
564 SmallVector<Attribute> operands;
565 for (Value operand : op.getOperands()) {
566 operands.push_back(env.lookup(operand).constant);
568 if (llvm::all_of(operands, [](Attribute attr) {
return attr !=
nullptr; })) {
569 SmallVector<OpFoldResult> folded;
571 for (
auto [index, value] : llvm::enumerate(folded)) {
572 if (
auto attr = dyn_cast<Attribute>(value)) {
573 results[index] = attr;
575 results[index] = env.lookup(llvm::cast<Value>(value));
583 std::optional<SmallVector<Attribute>>
584 arrayCoordinates(array::ArrayAccessOpInterface access,
const Environment &env) {
586 llvm::cast<array::ArrayType>(typeResolver.resolveType(access.
getArrRef().getType()));
587 SmallVector<int64_t> shape(type.getShape());
588 auto concreteShape = storage.getShape(env.lookup(access.
getArrRef()));
589 if (!concreteShape.empty()) {
590 shape.assign(concreteShape.begin(), concreteShape.end());
592 SmallVector<Attribute> path;
593 for (
auto [i, index] : llvm::enumerate(access.
getIndices())) {
594 auto coordinate = getKnownInteger(env.lookup(index));
595 if (!coordinate || *coordinate < 0 || i >= shape.size() || shape[i] < 0 ||
596 *coordinate >= shape[i]) {
599 path.push_back(IntegerAttr::get(IndexType::get(context()), *coordinate));
607 allocationShape(array::ArrayType type, llvm::function_ref<Attribute(AffineMapAttr)> resolveMap) {
608 auto resolved = llvm::cast<array::ArrayType>(typeResolver.resolveType(type));
609 SmallVector<int64_t> shape(resolved.getShape());
611 if (
auto map = dyn_cast<AffineMapAttr>(dimension)) {
612 auto extent = getKnownInteger(resolveMap(map));
613 if (extent && *extent >= 0) {
623 void recordPodShapes(
624 Type type,
const DiscoveryValue &value, SmallVector<Attribute> &path,
625 llvm::function_ref<Attribute(AffineMapAttr)> resolveMap
627 if (
auto podType = dyn_cast<pod::PodType>(type)) {
628 for (
auto record : podType.getRecords()) {
629 path.push_back(record.getName());
630 recordPodShapes(record.getType(), value, path, resolveMap);
633 }
else if (
auto arrayType = dyn_cast<array::ArrayType>(type)) {
634 storage.setShape(value, allocationShape(arrayType, resolveMap), path);
635 }
else if (
auto structure = dyn_cast<StructType>(type)) {
636 if (
auto parameters = structure.
getParams()) {
637 for (Attribute parameter : parameters) {
638 if (
auto map = dyn_cast<AffineMapAttr>(parameter)) {
643 }
else if (isa<TypeVarType>(type)) {
645 auto resolved = typeResolver.resolveType(type);
646 if (resolved != type) {
647 recordPodShapes(resolved, value, path, [](AffineMapAttr) {
return Attribute(); });
655 evaluateStorage(Operation &op,
const Environment &env, SmallVector<DiscoveryValue> &results) {
656 return llvm::TypeSwitch<Operation *, bool>(&op)
657 .Case<array::CreateArrayOp>([&](
auto allocation) {
658 auto value = storage.create();
661 auto shape = allocationShape(allocation.getType(), [&](AffineMapAttr map) {
662 if (group >= allocation.getMapOperands().size()) {
665 SmallVector<Attribute> operands;
666 for (Value operand : allocation.getMapOperands()[group++]) {
667 operands.push_back(env.lookup(operand).constant);
669 return typeResolver.resolveAffineArgument(map, operands);
671 storage.setShape(value, shape);
672 if (llvm::any_of(shape, [](int64_t size) {
return size <= 0; })) {
675 for (
auto [linear, element] : llvm::enumerate(allocation.getElements())) {
678 SmallVector<Attribute> path(allocation.getType().getDimensionSizes().size());
679 uint64_t offset = linear;
680 for (
size_t i = path.size(); i > 0; --i) {
681 path[i - 1] = IntegerAttr::get(
682 IndexType::get(context()),
static_cast<int64_t
>(offset % shape[i - 1])
684 offset /= shape[i - 1];
686 storage.write(value, path, env.lookup(element));
690 .Case<pod::NewPodOp>([&](
auto allocation) {
691 auto value = storage.create();
693 SmallVector<Attribute> path;
694 recordPodShapes(allocation.getType(), value, path, [&](AffineMapAttr map) {
695 if (group >= allocation.getMapOperands().size()) {
698 SmallVector<Attribute> operands;
699 for (Value operand : allocation.getMapOperands()[group++]) {
700 operands.push_back(env.lookup(operand).constant);
702 return typeResolver.resolveAffineArgument(map, operands);
704 for (
auto record : allocation.getInitializedRecordValues()) {
705 storage.write(value, {StringAttr::get(context(), record.name)}, env.lookup(record.value));
707 results[0] = std::move(value);
710 .Case<pod::ReadPodOp>([&](
auto read) {
711 results[0] = storage.
read(
712 env.lookup(read.getPodRef()), {StringAttr::get(context(), read.getRecordName())},
713 isa<array::ArrayType, pod::PodType>(typeResolver.resolveType(read.getType()))
717 .Case<pod::WritePodOp>([&](
auto write) {
719 env.lookup(write.getPodRef()), {StringAttr::get(context(), write.getRecordName())},
720 env.lookup(write.getValue())
724 .Case<array::ArrayAccessOpInterface>([&](
auto access) {
725 auto coordinates = arrayCoordinates(access, env);
727 if (isa<array::ReadArrayOp, array::ExtractArrayOp>(op)) {
729 results[0] = storage.
read(
731 isa<array::ArrayType, pod::PodType>(
732 typeResolver.resolveType(op.getResult(0).getType())
736 }
else if (coordinates) {
737 auto value = isa<array::WriteArrayOp>(op)
738 ? llvm::cast<array::WriteArrayOp>(op).getRvalue()
739 : llvm::cast<array::InsertArrayOp>(op).getRvalue();
740 storage.
write(
array, *coordinates, env.lookup(value));
746 .Case<UnifiableCastOp>([&](
auto conversion) {
747 results[0] = env.lookup(conversion.getInput());
749 }).Default([](Operation *) {
return false; });
755 evaluateOperation(Operation &op,
const Environment &env, SmallVector<DiscoveryValue> &results) {
756 if (visitedOperations) {
757 visitedOperations->push_back(&op);
759 if (
auto branch = dyn_cast<scf::IfOp>(op)) {
760 if (failed(evaluateIf(branch, env, results))) {
763 }
else if (
auto forLoop = dyn_cast<scf::ForOp>(op)) {
764 if (failed(evaluateFor(forLoop, env, results))) {
767 }
else if (
auto whileLoop = dyn_cast<scf::WhileOp>(op)) {
768 if (failed(evaluateWhile(whileLoop, env, results))) {
771 }
else if (op.getNumRegions()) {
772 return op.emitError(
"unsupported control flow in specialization discovery");
776 if (evaluateStorage(op, env, results)) {
778 }
else if (
auto read = dyn_cast<ConstReadOp>(op)) {
779 auto value = evaluateConstRead(read);
784 }
else if (isMemoryEffectFree(&op) || isa<cast::FeltToIndexOp, cast::IntToFeltOp>(op)) {
785 evaluateFoldableOp(op, env, results);
787 if (
auto call = dyn_cast<function::CallOp>(op)) {
788 if (failed(discoverCallTypes(call, env))) {
792 if (!isMemoryEffectFree(&op) &&
796 for (Value operand : op.getOperands()) {
797 if (isa<array::ArrayType, pod::PodType>(typeResolver.resolveType(operand.getType()))) {
798 storage.
forget(env.lookup(operand));
802 if (isa<function::CallOp>(op)) {
806 for (Type type : op.getResultTypes()) {
807 if (failed(collectTypes(&op, type))) {
817 LogicalResult evaluateBlock(
818 Block &body,
Environment env, SmallVectorImpl<DiscoveryValue> &yielded,
819 Attribute *condition =
nullptr
821 for (Operation &op : body) {
822 if (
auto terminator = dyn_cast<scf::ConditionOp>(op)) {
824 return op.emitError(
"scf.condition outside a while condition region");
826 *condition = env.lookup(terminator.getCondition()).constant;
827 for (Value operand : terminator.getArgs()) {
828 yielded.push_back(env.lookup(operand));
832 if (isa<YieldOp, scf::YieldOp, function::ReturnOp>(op)) {
833 for (Value operand : op.getOperands()) {
834 yielded.push_back(env.lookup(operand));
838 if (op.hasTrait<OpTrait::IsTerminator>()) {
839 return op.emitError(
"unsupported control flow in specialization discovery");
841 SmallVector<DiscoveryValue> results(op.getNumResults());
842 if (failed(evaluateOperation(op, env, results))) {
845 bindResults(op.getResults(), results, env);
851 MLIRContext *context() {
return source->getContext(); }
855 SmallVectorImpl<Operation *> *visited,
859 visitedOperations(visited), callTargets(targets) {}
862 FailureOr<StructSpecializationDiscovery::Bindings> evaluateBindings() {
865 SmallVector<DiscoveryValue> values;
866 if (failed(evaluateBlock(expr.getInitializerRegion().front(),
Environment(), values))) {
869 if (values.size() == 1 && values[0].constant) {
870 bindings[FlatSymbolRefAttr::get(expr.getSymNameAttr())] = values[0].constant;
879 FailureOr<StructSpecializationDiscovery::Requests> run() {
880 if (failed(evaluateBindings())) {
883 if (
auto structure = dyn_cast<StructDefOp>(
source)) {
884 for (
auto member : structure.getOps<
MemberDefOp>()) {
885 if (failed(collectTypes(member, member.getType()))) {
890 SmallVector<function::FuncDefOp> functions;
891 if (
auto structure = dyn_cast<StructDefOp>(
source)) {
894 functions.push_back(llvm::cast<function::FuncDefOp>(
source));
897 if (!
function.getBody().hasOneBlock()) {
899 "specialization discovery requires a defined single-block function; "
900 "unstructured control flow is unsupported"
903 for (Type type :
function.getFunctionType().getInputs()) {
904 if (failed(collectTypes(
function, type))) {
908 for (Type type :
function.getFunctionType().getResults()) {
909 if (failed(collectTypes(
function, type))) {
913 SmallVector<DiscoveryValue> values;
918 for (
auto [call, family] : rolledCalls) {
919 auto key = getFamilyKey(call, family);
923 if (!knownFamilies.contains(*key)) {
924 return call.emitError(
"no known source for affine struct call argument ") << family;
926 auto candidates = familyCandidates.lookup(*key);
928 if (failed(recordRequest(call, candidate))) {
933 callTargets->push_back({call, candidates});
936 return std::move(requests);
948 if (
auto constant = dyn_cast<felt::FeltConstAttr>(binding)) {
949 if (constant.getType() != readType) {
950 return read.emitError(
"cannot read felt template binding ")
951 << read.
getConstNameAttr() <<
" of type " << constant.getType() <<
" as " << readType
952 << (isa<felt::FeltType>(readType) ?
"; read the binding using its declared felt type"
953 :
"; use an explicit cast");
955 if (
auto reduced = constant.getReducedValue()) {
956 return Attribute(felt::FeltConstAttr::get(read.getContext(), *reduced, constant.getType()));
960 if (
auto integer = dyn_cast<IntegerAttr>(binding)) {
961 const APInt &number = integer.getValue();
962 if (
auto type = dyn_cast<felt::FeltType>(readType)) {
964 auto value = integer.getType().isSignlessInteger(1)
965 ? llvm::DynamicAPInt(number.isOne() ? 1 : 0)
966 :
llvm::DynamicAPInt(number);
967 if (type.hasField()) {
968 const Field &field = type.getField();
969 return Attribute(felt::FeltConstAttr::get(read.getContext(), field.reduce(value), type));
972 return read.emitError(
"cannot read negative template binding ")
975 return Attribute(felt::FeltConstAttr::get(read.getContext(), value, type));
977 if (readType.isSignlessInteger(1)) {
978 if (!number.isZero() && !number.isOne()) {
979 return read.emitError(
"cannot read template binding ")
981 <<
" as i1: expected zero or one; use an explicit cast";
983 return Attribute(IntegerAttr::get(readType, number.isOne() ? 1 : 0));
985 if (readType.isIndex()) {
986 auto bits = integer.getType().isSignlessInteger(1) ? number.zext(64) : number.sextOrTrunc(64);
987 return Attribute(IntegerAttr::get(readType, bits));
990 return read.emitError(
"cannot read template binding ")
991 << read.
getConstNameAttr() <<
" with value \"" << binding <<
"\" as type " << readType;
994FailureOr<StructSpecializationDiscovery::Bindings>
996 return DiscoveryRun(
source, bindings,
nullptr).evaluateBindings();
1000 Operation *
source,
const Bindings &bindings, SmallVectorImpl<Operation *> *visitedOperations,
1003 return DiscoveryRun(
source, bindings, visitedOperations, callTargets).run();
This file implements helpers for DynamicAPInt operations and conversions that LLVM does not provide.
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 and configuration files Object form shall mean any form resulting from mechanical transformation or translation of a Source including but not limited to compiled object generated and conversions to other media types Work shall mean the work of whether in Source or Object made available under the as indicated by a copyright notice that is included in or attached to the whether in Source or Object that is based or other modifications as a an original work of authorship For the purposes of this Derivative Works shall not include works that remain separable or merely the Work and Derivative Works thereof Contribution shall mean any work of including the original version of the Work and any modifications or additions to that Work or Derivative Works that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner For the purposes of this definition
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
::mlir::Operation::operand_range getIndices()
Gets the operand range containing the index for each dimension.
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
Gets the SSA Value for the referenced array.
ArrayType cloneWith(std::optional<::llvm::ArrayRef< int64_t > > shape, ::mlir::Type elementType) const
Clone this type with the given shape and element type.
::mlir::Type getElementType() const
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
::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
::mlir::Operation::operand_range getArgOperands()
::mlir::OperandRangeRange getMapOperands()
::mlir::FlatSymbolRefAttr getConstNameAttr()
::mlir::FlatSymbolRefAttr getNameRef() const
void write(const DiscoveryValue &destination, llvm::ArrayRef< mlir::Attribute > path, const DiscoveryValue &value)
Replace a subtree, clearing old facts even when the new value is unknown.
DiscoveryValue read(const DiscoveryValue &source, llvm::ArrayRef< mlir::Attribute > path, bool aggregate)
Read a scalar leaf or snapshot an aggregate subtree into an independent root.
void forget(const DiscoveryValue &value)
Forget the contents of a potentially modified or escaping local aggregate.
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
llvm::SmallVector< StructSpecializationRequest > Requests
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
llvm::DenseMap< mlir::Value, BoundValue > Environment
mlir::LogicalResult evaluateScalarOperation(mlir::Operation *op, llvm::ArrayRef< mlir::Attribute > operands, llvm::SmallVectorImpl< mlir::OpFoldResult > &results)
Evaluate a scalar operation using caller-supplied operand constants.
bool isInvariantTrueCondition(mlir::Value condition)
Prove a Boolean condition true without any loop-carried bindings.
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.
bool hasNextStaticIteration(int64_t current, int64_t upper, int64_t step)
Whether another signed iteration exists after current, for current < upper and a positive step.
ArrayType flattenArrayElementType(ArrayType outerArrTy, Type elementType)
bool isConcreteStructParamAttr(mlir::Attribute attr, bool allowStructParams=true)
Return true if attr is a concrete argument for a parameterized struct type.
bool hasAffineMapAttr(Type type)