36#include <mlir/Dialect/Affine/IR/AffineOps.h>
37#include <mlir/Dialect/Affine/LoopUtils.h>
38#include <mlir/Dialect/Arith/IR/Arith.h>
39#include <mlir/Dialect/SCF/IR/SCF.h>
40#include <mlir/Dialect/SCF/Utils/Utils.h>
41#include <mlir/Dialect/Utils/StaticValueUtils.h>
42#include <mlir/IR/Attributes.h>
43#include <mlir/IR/BuiltinAttributes.h>
44#include <mlir/IR/BuiltinOps.h>
45#include <mlir/IR/BuiltinTypes.h>
46#include <mlir/Interfaces/InferTypeOpInterface.h>
47#include <mlir/Pass/PassManager.h>
48#include <mlir/Support/LLVM.h>
49#include <mlir/Support/LogicalResult.h>
50#include <mlir/Transforms/DialectConversion.h>
51#include <mlir/Transforms/GreedyPatternRewriteDriver.h>
52#include <mlir/Transforms/WalkPatternRewriteDriver.h>
54#include <llvm/ADT/APInt.h>
55#include <llvm/ADT/DenseMap.h>
56#include <llvm/ADT/DepthFirstIterator.h>
57#include <llvm/ADT/STLExtras.h>
58#include <llvm/ADT/SmallVector.h>
59#include <llvm/ADT/TypeSwitch.h>
60#include <llvm/Support/Debug.h>
66#define GEN_PASS_DEF_FLATTENINGPASS
73#define DEBUG_TYPE "llzk-flatten"
87class ConversionTracker {
92 struct PartialFuncInstantiation {
93 ArrayAttr concreteParamKey;
94 StringAttr templateName;
95 StringAttr functionName;
102 DenseMap<StructType, StructType> structInstantiations;
104 DenseMap<StructType, StructType> reverseInstantiations;
106 DenseSet<SymbolRefAttr> funcInstantiations;
109 DenseMap<Operation *, SmallVector<PartialFuncInstantiation>> partialFuncInstantiations;
112 DenseMap<StructType, SmallVector<Diagnostic>> delayedDiagnostics;
115 bool isModified()
const {
return modified; }
116 void resetModifiedFlag() { modified =
false; }
117 void updateModifiedFlag(
bool currStepModified) { modified |= currStepModified; }
122 auto forwardResult = structInstantiations.try_emplace(oldType, newType);
123 if (forwardResult.second) {
126 assert(!reverseInstantiations.contains(newType));
127 reverseInstantiations[newType] = oldType;
132 assert(forwardResult.first->getSecond() == newType);
134 assert(reverseInstantiations.lookup(newType) == oldType);
136 assert(structInstantiations.size() == reverseInstantiations.size());
140 std::optional<StructType> getInstantiation(
StructType oldType)
const {
141 auto cachedResult = structInstantiations.find(oldType);
142 if (cachedResult != structInstantiations.end()) {
143 return cachedResult->second;
149 void recordInstantiation(SymbolRefAttr funcName) {
150 funcInstantiations.insert(funcName);
155 std::optional<SymbolRefAttr>
156 lookupPartialFuncInstantiation(
FuncDefOp sourceFunc, ArrayAttr concreteParamKey)
const {
157 auto found = partialFuncInstantiations.find(sourceFunc.getOperation());
158 if (found == partialFuncInstantiations.end()) {
161 for (
const PartialFuncInstantiation &candidate : found->second) {
162 if (candidate.concreteParamKey == concreteParamKey) {
163 SmallVector<FlatSymbolRefAttr> calleeSuffix {
164 FlatSymbolRefAttr::get(candidate.templateName),
165 FlatSymbolRefAttr::get(candidate.functionName),
174 void recordPartialFuncInstantiation(
178 !lookupPartialFuncInstantiation(sourceFunc, concreteParamKey).has_value() &&
179 "partial function instantiation already cached"
181 partialFuncInstantiations[sourceFunc.getOperation()].push_back(
182 PartialFuncInstantiation {
192 void clearPartialFuncInstantiations() { partialFuncInstantiations.clear(); }
195 DenseSet<SymbolRefAttr> getInstantiatedDefinitionNames()
const {
196 DenseSet<SymbolRefAttr> instantiatedNames = funcInstantiations;
197 for (
const auto &[origRemoteTy, _] : structInstantiations) {
198 instantiatedNames.insert(origRemoteTy.getNameRef());
200 return instantiatedNames;
204 auto res = delayedDiagnostics.find(newType);
205 if (res != delayedDiagnostics.end()) {
211 delayedDiagnostics.erase(newType);
215 SmallVector<Diagnostic> &delayedDiagnosticSet(
StructType newType) {
216 return delayedDiagnostics[newType];
221 bool isLegalConversion(Type oldType, Type newType,
const char *patName)
const {
222 std::function<bool(Type, Type)> checkInstantiations = [&](Type oTy, Type nTy) {
224 if (
StructType oldStructType = llvm::dyn_cast<StructType>(oTy)) {
227 if (this->structInstantiations.lookup(oldStructType) == nTy) {
234 if (
StructType newStructType = llvm::dyn_cast<StructType>(nTy)) {
235 if (
auto preImage = this->reverseInstantiations.lookup(newStructType)) {
248 llvm::dbgs() <<
"[" << patName <<
"] Cannot replace old type " << oldType
249 <<
" with new type " << newType
250 <<
" because it does not define a compatible and more concrete type.\n";
255 template <
typename T,
typename U>
256 inline bool areLegalConversions(T oldTypes, U newTypes,
const char *patName)
const {
258 llvm::zip_equal(oldTypes, newTypes), [
this, &patName](std::tuple<Type, Type> oldThenNew) {
259 return this->isLegalConversion(std::get<0>(oldThenNew), std::get<1>(oldThenNew), patName);
267struct MatchFailureListener :
public RewriterBase::Listener {
268 bool hadFailure =
false;
270 ~MatchFailureListener()
override {}
272 void notifyMatchFailure(Location loc, function_ref<
void(Diagnostic &)> reasonCallback)
override {
275 InFlightDiagnostic diag = emitError(loc);
276 reasonCallback(*diag.getUnderlyingDiagnostic());
282applyAndFoldGreedily(ModuleOp modOp, ConversionTracker &tracker, RewritePatternSet &&patterns) {
283 bool currStepModified =
false;
284 MatchFailureListener failureListener;
285 LogicalResult result = applyPatternsGreedily(
286 modOp->getRegion(0), std::move(patterns),
287 GreedyRewriteConfig().enableFolding().setMaxIterations(20).setListener(&failureListener),
290 tracker.updateModifiedFlag(currStepModified);
291 return failure(result.failed() || failureListener.hadFailure);
295template <
bool AllowStructParams = true>
bool isConcreteAttr(Attribute a) {
299static bool calleeReferencesTemplateParam(
CallOp op) {
301 if (!callee || callee.getNestedReferences().size() != 1) {
305 if (!parentTemplate) {
316 ConversionTracker &tracker_;
318 SymbolTableCollection symTables;
319 bool reportMissing =
true;
321 FailureOr<StructType> genClone(
StructType typeAtCaller, ArrayRef<Attribute> typeAtCallerParams) {
322 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] attempting clone of " << typeAtCaller <<
'\n');
324 FailureOr<SymbolLookupResult<StructDefOp>> r =
325 typeAtCaller.
getDefinition(symTables, rootMod, reportMissing);
327 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: cannot find StructDefOp \n");
330 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] found definition\n";);
334 MLIRContext *ctx = origStruct.getContext();
336 assert(parentTemplate &&
"parameterized struct must be nested in a TemplateOp");
338 assert(parentModule &&
"TemplateOp must be nested in a ModuleOp");
341 DenseMap<Attribute, Attribute> paramNameToConcrete;
343 ArrayAttr reducedCallerParams =
nullptr;
344 SmallVector<Attribute> nonConcreteParams;
346 ArrayAttr paramNames = typeAtDef.
getParams();
350 assert(paramNames.size() == typeAtCallerParams.size());
352 for (
size_t i = 0, e = paramNames.size(); i < e; ++i) {
353 Attribute next = typeAtCallerParams[i];
354 if (isConcreteAttr<false>(next)) {
355 paramNameToConcrete[paramNames[i]] = next;
357 nonConcreteParams.push_back(next);
361 assert(nonConcreteParams.size() + paramNameToConcrete.size() == paramNames.size());
363 if (paramNameToConcrete.empty()) {
364 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: no concrete params \n");
367 if (!nonConcreteParams.empty()) {
368 reducedCallerParams = ArrayAttr::get(ctx, nonConcreteParams);
372 FailureOr<InstantiationLayout> layoutResult =
374 if (failed(layoutResult)) {
381 SmallVector<FlatSymbolRefAttr> typeAtCallerSymPieces =
getPieces(typeAtCaller.
getNameRef());
382 typeAtCallerSymPieces.pop_back();
398 symTables.getSymbolTable(parentModule).insert(newStruct, Block::iterator(parentTemplate));
400 typeAtCallerSymPieces.pop_back();
403 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
406 assert(newTemplate->getNumRegions() > 0 &&
"region exists");
411 FlatSymbolRefAttr nameSym = llvm::dyn_cast<FlatSymbolRefAttr>(name);
412 assert(nameSym &&
"expected FlatSymbolRefAttr");
414 Operation *symOp = symTables.getSymbolTable(parentTemplate).lookup(nameSym.getAttr());
415 assert(symOp &&
"symbol must exist");
416 newTemplate.insert(newTemplate.begin(), symOp->clone());
421 symTables.getSymbolTable(newTemplate).insert(newStruct);
422 symTables.getSymbolTable(parentModule).insert(newTemplate, Block::iterator(parentTemplate));
426 typeAtCallerSymPieces.back() = FlatSymbolRefAttr::get(newTemplate.
getSymNameAttr());
432 typeAtCallerSymPieces.push_back(
433 FlatSymbolRefAttr::get(newLocalType.
getNameRef().getLeafReference())
438 llvm::dbgs() <<
"[StructCloner] original def type: " << typeAtDef <<
'\n';
439 llvm::dbgs() <<
"[StructCloner] cloned def type: " << newStruct.
getType() <<
'\n';
440 llvm::dbgs() <<
"[StructCloner] original remote type: " << typeAtCaller <<
'\n';
441 llvm::dbgs() <<
"[StructCloner] cloned local type: " << newLocalType <<
'\n';
442 llvm::dbgs() <<
"[StructCloner] cloned remote type: " << newRemoteType <<
'\n';
449 newStruct, typeAtDef, paramNameToConcrete, tracker_.delayedDiagnosticSet(newLocalType)
451 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] instantiating body of struct failed \n");
454 return newRemoteType;
458 StructCloner(ConversionTracker &tracker, ModuleOp root)
459 : tracker_(tracker), rootMod(root), symTables() {}
461 FailureOr<StructType> createInstantiatedClone(
StructType orig) {
462 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] orig: " << orig <<
'\n');
463 if (ArrayAttr params = orig.
getParams()) {
464 return genClone(orig, params.getValue());
466 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: nullptr for params \n");
470 void enableReportMissing() { reportMissing =
true; }
472 void disableReportMissing() { reportMissing =
false; }
475class DisableReportMissing;
477class ParameterizedStructUseTypeConverter :
public TypeConverter {
478 ConversionTracker &tracker_;
481 friend DisableReportMissing;
484 ParameterizedStructUseTypeConverter(ConversionTracker &tracker, ModuleOp root)
485 : TypeConverter(), tracker_(tracker), cloner(tracker, root) {
487 addConversion([](Type inputTy) {
return inputTy; });
491 llvm::dbgs() <<
"[ParameterizedStructUseTypeConverter] attempting conversion of "
495 if (
auto opt = tracker_.getInstantiation(inputTy)) {
501 FailureOr<StructType> cloneRes = cloner.createInstantiatedClone(inputTy);
502 if (failed(cloneRes)) {
507 llvm::dbgs() <<
"[ParameterizedStructUseTypeConverter] instantiating " << inputTy
508 <<
" as " << newTy <<
'\n'
510 tracker_.recordInstantiation(inputTy, newTy);
514 addConversion([
this](
ArrayType inputTy) {
515 return inputTy.cloneWith(convertType(inputTy.getElementType()));
520class CallStructFuncPattern :
public OpConversionPattern<CallOp> {
521 ConversionTracker &tracker_;
524 CallStructFuncPattern(TypeConverter &converter, MLIRContext *ctx, ConversionTracker &tracker)
526 : OpConversionPattern<CallOp>(converter, ctx, 1), tracker_(tracker) {}
528 LogicalResult matchAndRewrite(
529 CallOp op, OpAdaptor adapter, ConversionPatternRewriter &rewriter
531 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] CallOp: " << op <<
'\n');
534 SmallVector<Type> newResultTypes;
535 if (failed(getTypeConverter()->convertTypes(op.getResultTypes(), newResultTypes))) {
536 return op->emitError(
"Could not convert Op result types.");
539 llvm::dbgs() <<
"[CallStructFuncPattern] newResultTypes: "
549 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] newStTy: " << newStTy <<
'\n');
550 calleeAttr =
appendLeaf(newStTy.getNameRef(), calleeAttr.getLeafReference());
551 tracker_.reportDelayedDiagnostics(newStTy, op);
555 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] newStTy: " << newStTy <<
'\n');
556 calleeAttr =
appendLeaf(newStTy.getNameRef(), calleeAttr.getLeafReference());
560 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] replaced " << op);
562 rewriter, op, newResultTypes, calleeAttr, adapter.
getMapOperands(),
566 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
572class MemberDefOpPattern :
public OpConversionPattern<MemberDefOp> {
574 MemberDefOpPattern(TypeConverter &converter, MLIRContext *ctx, ConversionTracker &)
576 : OpConversionPattern<MemberDefOp>(converter, ctx, 1) {}
578 LogicalResult matchAndRewrite(
579 MemberDefOp op, OpAdaptor , ConversionPatternRewriter &rewriter
581 LLVM_DEBUG(llvm::dbgs() <<
"[MemberDefOpPattern] MemberDefOp: " << op <<
'\n');
583 Type oldMemberType = op.
getType();
584 Type newMemberType = getTypeConverter()->convertType(oldMemberType);
585 if (oldMemberType == newMemberType) {
588 rewriter.modifyOpInPlace(op, [&op, &newMemberType]() { op.
setType(newMemberType); });
596 ParameterizedStructUseTypeConverter &tyConv;
599 explicit DisableReportMissing(ParameterizedStructUseTypeConverter &tc) : tyConv(tc) {}
601 void checkStarted()
override { tyConv.cloner.disableReportMissing(); }
603 void checkEnded(
bool)
override { tyConv.cloner.enableReportMissing(); }
606LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
607 MLIRContext *ctx = modOp.getContext();
608 ParameterizedStructUseTypeConverter tyConv(tracker, modOp);
609 DisableReportMissing drm(tyConv);
612 patterns.add<CallStructFuncPattern, MemberDefOpPattern>(tyConv, ctx, tracker);
613 return applyPartialConversion(modOp, target, std::move(patterns));
618LogicalResult instantiateMainStruct(ModuleOp modOp, ConversionTracker &tracker) {
620 if (failed(mainTypeOpt)) {
629 StructCloner cloner(tracker, modOp);
630 FailureOr<StructType> cloneRes = cloner.createInstantiatedClone(mainType);
631 if (failed(cloneRes)) {
635 StructType instantiatedMainType = cloneRes.value();
636 tracker.recordInstantiation(mainType, instantiatedMainType);
637 modOp->setAttr(
MAIN_ATTR_NAME, TypeAttr::get(instantiatedMainType));
647inline static LogicalResult failIncompatibleInferredParam(
651 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
652 <<
"': incompatible with specified param type. MUST FAIL!\n"
654 return rewriter.notifyMatchFailure(op, [¶mName, ¶mOp](Diagnostic &diag) {
655 diag.append(
"inferred value for parameter '")
657 .append(
"' is incompatible with specified param type")
658 .attachNote(paramOp.getLoc())
659 .append(
"template parameter declared here");
665class WildcardTypeBodyInferer final {
666 SymbolTableCollection &symTables_;
667 const DenseMap<Attribute, Attribute> ¶mNameToConcrete_;
668 SmallVector<std::pair<Operation *, FlatSymbolRefAttr>> activeInferences_;
671 WildcardTypeBodyInferer(
672 SymbolTableCollection &symTables,
const DenseMap<Attribute, Attribute> ¶mNameToConcrete
674 : symTables_(symTables), paramNameToConcrete_(paramNameToConcrete) {}
676 std::optional<Attribute> infer(
FuncDefOp func, FlatSymbolRefAttr paramName) {
677 if (llvm::any_of(activeInferences_, [&](
const auto &e) {
678 return e.first == func.getOperation() && e.second == paramName;
682 activeInferences_.emplace_back(func.getOperation(), paramName);
685 std::optional<Attribute> inferred;
686 bool ambiguous =
false;
690 auto noteCandidate = [&inferred, &ambiguous](Attribute candidate) {
691 if (!candidate || !isConcreteAttr(candidate)) {
692 return WalkResult::advance();
694 if (!inferred.has_value()) {
695 inferred = candidate;
696 return WalkResult::advance();
698 if (*inferred != candidate) {
700 return WalkResult::interrupt();
702 return WalkResult::advance();
705 WalkResult walkResult = func.walk([&](Operation *bodyOp) {
706 if (
auto castOp = llvm::dyn_cast<UnifiableCastOp>(bodyOp)) {
707 Type inputTy = tyConv.convertType(castOp.getInput().getType());
708 Type resultTy = tyConv.convertType(castOp.getResult().getType());
709 if (
auto inputTvar = llvm::dyn_cast<TypeVarType>(inputTy);
710 inputTvar && inputTvar.getNameRef() == paramName &&
isConcreteType(resultTy)) {
711 return noteCandidate(TypeAttr::get(resultTy));
713 if (
auto resultTvar = llvm::dyn_cast<TypeVarType>(resultTy);
714 resultTvar && resultTvar.getNameRef() == paramName &&
isConcreteType(inputTy)) {
715 return noteCandidate(TypeAttr::get(inputTy));
717 return WalkResult::advance();
720 auto nestedCall = llvm::dyn_cast<CallOp>(bodyOp);
722 return WalkResult::advance();
725 FailureOr<SymbolLookupResult<FuncDefOp>> nestedTgtOpt =
726 nestedCall.getCalleeTarget(symTables_);
727 if (failed(nestedTgtOpt)) {
728 return WalkResult::advance();
730 FuncDefOp nestedTgt = nestedTgtOpt->get();
731 auto nestedTemplate = llvm::dyn_cast<TemplateOp>(nestedTgt->getParentOp());
732 if (!nestedTemplate) {
733 return WalkResult::advance();
736 TypeRange nestedResultTypes = nestedTgt.
getFunctionType().getResults();
737 for (
auto [result, nestedResultTy] :
738 llvm::zip_equal(nestedCall.getResults(), nestedResultTypes)) {
739 Type convertedResultTy = tyConv.convertType(result.getType());
740 auto resultTvar = llvm::dyn_cast<TypeVarType>(convertedResultTy);
741 auto nestedTvar = llvm::dyn_cast<TypeVarType>(nestedResultTy);
742 if (!resultTvar || !nestedTvar || resultTvar.getNameRef() != paramName) {
745 if (std::optional<Attribute> candidate = inferFromExplicitNestedCallParams(
746 nestedCall, nestedTemplate, nestedTvar.getNameRef(), tyConv
748 WalkResult candidateResult = noteCandidate(*candidate);
749 if (candidateResult.wasInterrupted()) {
750 return candidateResult;
754 if (std::optional<Attribute> candidate = infer(nestedTgt, nestedTvar.getNameRef())) {
755 WalkResult candidateResult = noteCandidate(*candidate);
756 if (candidateResult.wasInterrupted()) {
757 return candidateResult;
761 return WalkResult::advance();
764 activeInferences_.pop_back();
765 if (ambiguous || (walkResult.wasInterrupted() && !inferred.has_value())) {
772 std::optional<Attribute> inferFromExplicitNestedCallParams(
773 CallOp nestedCall,
TemplateOp nestedTemplate, FlatSymbolRefAttr nestedParamName,
781 for (
auto [paramOp, attr] :
783 auto paramName = FlatSymbolRefAttr::get(paramOp.
getSymNameAttr());
784 if (paramName != nestedParamName) {
787 Attribute convertedAttr = tyConv.
convertAttr(attr);
788 return isConcreteAttr(convertedAttr) ? std::make_optional(convertedAttr) : std::nullopt;
797static LogicalResult applyBodyConversions(
798 CallOp op,
FuncDefOp newFunc,
const DenseMap<Attribute, Attribute> ¶mNameToConcrete
800 SmallVector<Diagnostic> delayedDiagnostics;
804 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] instantiated clone: " << newFunc <<
'\n');
807 SymbolTableCollection tables;
808 WalkResult res = newFunc.walk([&tables](
CallOp nestedCall) {
811 return failure(res.wasInterrupted());
814class InstantiateFuncAtCallOp final :
public OpRewritePattern<CallOp> {
815 ConversionTracker &tracker_;
818 InstantiateFuncAtCallOp(MLIRContext *ctx, ConversionTracker &tracker)
819 : OpRewritePattern<CallOp>(ctx), tracker_(tracker) {}
821 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
822 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] op: " << op <<
'\n');
824 if (calleeReferencesTemplateParam(op)) {
829 SymbolTableCollection symTables;
830 FailureOr<SymbolLookupResult<FuncDefOp>> callTgtOpt = op.
getCalleeTarget(symTables);
831 if (failed(callTgtOpt)) {
832 return rewriter.notifyMatchFailure(op, [](Diagnostic &diag) {
833 diag <<
"could not find target function for call";
839 TemplateOp parentTemplate = llvm::dyn_cast<TemplateOp>(callTgt->getParentOp());
840 if (!parentTemplate) {
844 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] target function in template "
855 FailureOr<UnificationMap> unifyResult = unifyTypeSignature(op, callTgt, rewriter);
856 if (failed(unifyResult)) {
860 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unifications of types: "
865 DenseMap<Attribute, Attribute> paramNameToConcrete;
866 if (failed(collectConcreteTemplateParams(
867 op, rewriter, symTables, callTgt, parentTemplate, unifyResult.value(),
873 if (paramNameToConcrete.empty()) {
874 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] skip: no concrete params\n");
880 FailureOr<InstantiationLayout> layoutResult =
882 if (failed(layoutResult)) {
887 assert(parentModule &&
"TemplateOp must be nested in a ModuleOp");
890 FailureOr<SymbolRefAttr> newCalleeAttr =
893 op, rewriter, symTables, callTgt, parentTemplate, parentModule,
896 : instantiatePartially(
897 op, rewriter, symTables, callTgt, parentTemplate, parentModule, layout,
898 paramNameToConcrete, tracker_
900 if (failed(newCalleeAttr)) {
904 tracker_.recordInstantiation(originalCalleeAttr);
907 rewriter.modifyOpInPlace(op, [&op, &newCalleeAttr, &layout]() {
909 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] updating callee from " << op.
getCalleeAttr()
910 <<
" to " << *newCalleeAttr <<
'\n';
915 tracker_.updateModifiedFlag(
true);
922 static FailureOr<UnificationMap>
923 unifyTypeSignature(
CallOp op,
FuncDefOp callTgt, PatternRewriter &rewriter) {
924 FailureOr<UnificationMap> unifyResult = op.unifyTypeSignature(callTgt.
getFunctionType());
925 if (succeeded(unifyResult)) {
928 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
929 diag.append(
"target function type does not unify with call type ")
930 .append(op.getTypeSignature())
931 .attachNote(callTgt.getLoc())
932 .append(
"target function declared here");
938 static LogicalResult collectConcreteTemplateParams(
939 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
941 DenseMap<Attribute, Attribute> ¶mNameToConcrete
946 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] TemplateParamsAttr: " << callParams <<
'\n'
949 auto recordConcreteParam = [&](FlatSymbolRefAttr paramName,
TemplateParamOp paramOp,
950 Attribute concreteValue) {
951 if (failed(op.verifyTemplateParamValueCompatibility(concreteValue, paramOp))) {
952 return failIncompatibleInferredParam(op, rewriter, paramName, paramOp);
954 paramNameToConcrete[paramName] = concreteValue;
960 for (
auto paramOp : realParams) {
961 auto paramName = FlatSymbolRefAttr::get(paramOp.getSymNameAttr());
963 if (!inferredValOpt.has_value()) {
965 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
970 Attribute inferredVal = *inferredValOpt;
972 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] inferredVal: " << inferredVal <<
'\n'
974 if (!isConcreteAttr(inferredVal)) {
976 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
977 <<
"': not concrete, " << inferredVal <<
'\n'
981 if (failed(recordConcreteParam(paramName, paramOp, inferredVal))) {
990 assert((callParams.size() == llvm::range_size(realParams)) &&
"per CallOpVerifier");
991 if (failed(op.verifyTemplateParamValuesCompatibility(realParams))) {
992 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
993 diag.append(
"incompatible with specified param type(s)");
996 if (failed(op.verifyTemplateParamsMatchInferred(realParams, unifyResult))) {
997 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
998 diag.append(
"incompatible with inferred param value(s)");
1004 SmallVector<std::pair<TemplateParamOp, FlatSymbolRefAttr>> wildcardParams;
1005 for (
auto [paramOp, attr] : llvm::zip_equal(realParams, callParams.getValue())) {
1006 auto paramName = FlatSymbolRefAttr::get(paramOp.getSymNameAttr());
1009 paramNameToConcrete[paramName] = attr;
1015 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
1016 <<
"': not concrete, " << attr <<
'\n'
1020 wildcardParams.emplace_back(paramOp, paramName);
1023 WildcardTypeBodyInferer bodyInferer(symTables, paramNameToConcrete);
1024 for (
auto [paramOp, paramName] : wildcardParams) {
1026 if (inferredValOpt.has_value() && isConcreteAttr(*inferredValOpt)) {
1028 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] inferredVal: " << *inferredValOpt <<
'\n'
1030 if (failed(recordConcreteParam(paramName, paramOp, *inferredValOpt))) {
1036 inferredValOpt = bodyInferer.infer(callTgt, paramName);
1037 if (inferredValOpt.has_value() && isConcreteAttr(*inferredValOpt)) {
1039 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body-inferred value for param '"
1040 << paramName <<
"': " << *inferredValOpt <<
'\n'
1042 if (failed(recordConcreteParam(paramName, paramOp, *inferredValOpt))) {
1052 static FailureOr<SymbolRefAttr> instantiateFully(
1053 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
1054 TemplateOp parentTemplate, ModuleOp parentModule, StringRef templateNameWithAttrs,
1055 const DenseMap<Attribute, Attribute> ¶mNameToConcrete
1057 MLIRContext *ctx = op.getContext();
1058 std::string newFuncName =
1059 (mlir::Twine(templateNameWithAttrs) +
"_" + callTgt.
getSymName()).str();
1060 StringRef actualNewFuncName = newFuncName;
1061 if (!symTables.getSymbolTable(parentModule).lookup(newFuncName)) {
1066 symTables.getSymbolTable(parentModule).insert(newFunc, Block::iterator(parentTemplate));
1069 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] created full instantiation function: "
1070 << actualNewFuncName <<
'\n'
1072 if (failed(applyBodyConversions(op, newFunc, paramNameToConcrete))) {
1074 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body conversion failed for "
1075 << actualNewFuncName <<
'\n'
1078 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1079 diag.append(
"failure while creating instantiated function '", actualNewFuncName,
'\'');
1084 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] reusing full instantiation function: "
1085 << actualNewFuncName <<
'\n'
1093 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1094 symPieces.pop_back();
1095 symPieces.pop_back();
1096 symPieces.push_back(FlatSymbolRefAttr::get(StringAttr::get(ctx, actualNewFuncName)));
1104 static FailureOr<SymbolRefAttr> instantiatePartially(
1105 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
1107 const DenseMap<Attribute, Attribute> ¶mNameToConcrete, ConversionTracker &tracker
1109 if (
auto cached = tracker.lookupPartialFuncInstantiation(callTgt, layout.
concreteParamKey)) {
1111 SmallVector<FlatSymbolRefAttr> cachedSuffix =
getPieces(*cached);
1112 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1113 assert(cachedSuffix.size() == 2 &&
"cached callee suffix must contain template and function");
1114 symPieces.pop_back();
1115 symPieces.pop_back();
1116 symPieces.push_back(cachedSuffix[0]);
1117 symPieces.push_back(cachedSuffix[1]);
1120 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] reusing partial instantiation: "
1121 << cachedCallee <<
'\n'
1123 return cachedCallee;
1125 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
1128 assert(newTemplate->getNumRegions() > 0 &&
"region exists");
1131 Block &newTemplateBody = newTemplate.
getBodyRegion().front();
1133 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
1134 Operation *paramOp = symTables.getSymbolTable(parentTemplate).lookup(nameSym.getAttr());
1135 assert(paramOp &&
"symbol must exist");
1136 newTemplateBody.push_back(paramOp->clone());
1145 symTables.getSymbolTable(newTemplate).insert(newFunc);
1146 symTables.getSymbolTable(parentModule).insert(newTemplate, Block::iterator(parentTemplate));
1147 if (failed(applyBodyConversions(op, newFunc, paramNameToConcrete))) {
1148 std::string newFuncName = newFunc.
getSymName().str();
1150 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body conversion failed for " << newFuncName
1153 newTemplate->erase();
1154 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1155 diag.append(
"failure while creating instantiated function '", newFuncName,
'\'');
1161 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1162 symPieces.pop_back();
1163 symPieces.pop_back();
1164 symPieces.push_back(FlatSymbolRefAttr::get(newTemplate.
getSymNameAttr()));
1165 symPieces.push_back(FlatSymbolRefAttr::get(newFunc.
getSymNameAttr()));
1169 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] created partial instantiation: " << newCallee
1173 tracker.recordPartialFuncInstantiation(callTgt, layout.
concreteParamKey, newTemplate, newFunc);
1178LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1179 MLIRContext *ctx = modOp.getContext();
1180 RewritePatternSet patterns(ctx);
1181 patterns.add<InstantiateFuncAtCallOp>(ctx, tracker);
1182 MatchFailureListener failureListener;
1183 walkAndApplyPatterns(modOp, std::move(patterns), &failureListener);
1184 return failure(failureListener.hadFailure);
1192template <HasInterface<LoopLikeOpInterface> OpClass>
1193class LoopUnrollPattern :
public OpRewritePattern<OpClass> {
1195 using OpRewritePattern<OpClass>::OpRewritePattern;
1197 LogicalResult matchAndRewrite(OpClass loopOp, PatternRewriter &rewriter)
const override {
1198 if (
auto maybeConstant = getConstantTripCount(loopOp)) {
1199 uint64_t tripCount = *maybeConstant;
1200 if (tripCount == 0) {
1201 rewriter.eraseOp(loopOp);
1203 }
else if (tripCount == 1) {
1204 return loopOp.promoteIfSingleIteration(rewriter);
1206 return loopUnrollByFactor(loopOp, tripCount);
1214 static std::optional<int64_t> getConstantTripCount(LoopLikeOpInterface loopOp) {
1215 std::optional<OpFoldResult> lbVal = loopOp.getSingleLowerBound();
1216 std::optional<OpFoldResult> ubVal = loopOp.getSingleUpperBound();
1217 std::optional<OpFoldResult> stepVal = loopOp.getSingleStep();
1218 if (!lbVal.has_value() || !ubVal.has_value() || !stepVal.has_value()) {
1219 return std::nullopt;
1221 bool isSigned =
true;
1222 if (
auto forOp = dyn_cast<scf::ForOp>(loopOp.getOperation())) {
1223 isSigned = !forOp.getUnsignedCmp();
1225 std::optional<APInt> result = constantTripCount(
1226 lbVal.value(), ubVal.value(), stepVal.value(), isSigned, scf::computeUbMinusLb
1228 return result.has_value() ? std::optional<int64_t>(result->getZExtValue()) : std::nullopt;
1232LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1233 MLIRContext *ctx = modOp.getContext();
1234 RewritePatternSet patterns(ctx);
1235 patterns.add<LoopUnrollPattern<scf::ForOp>>(ctx);
1236 patterns.add<LoopUnrollPattern<affine::AffineForOp>>(ctx);
1238 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
1246std::optional<SmallVector<int64_t>> getConstantIntValues(ArrayRef<OpFoldResult> ofrs) {
1247 SmallVector<int64_t> res;
1248 for (OpFoldResult ofr : ofrs) {
1249 std::optional<int64_t> cv = getConstantIntValue(ofr);
1250 if (!cv.has_value()) {
1251 return std::nullopt;
1253 res.push_back(cv.value());
1258struct AffineMapFolder {
1260 OperandRangeRange mapOpGroups;
1261 DenseI32ArrayAttr dimsPerGroup;
1262 ArrayRef<Attribute> paramsOfStructTy;
1266 SmallVector<SmallVector<Value>> mapOpGroups;
1267 SmallVector<int32_t> dimsPerGroup;
1268 SmallVector<Attribute> paramsOfStructTy;
1271 static inline SmallVector<ValueRange> getConvertedMapOpGroups(Output out) {
1272 return llvm::map_to_vector(out.mapOpGroups, [](
const SmallVector<Value> &grp) {
1273 return ValueRange(grp);
1277 static LogicalResult
1278 fold(PatternRewriter &rewriter,
const Input &in, Output &out, Operation *op,
const char *aspect) {
1279 if (in.mapOpGroups.empty()) {
1284 assert(in.mapOpGroups.size() <= in.paramsOfStructTy.size());
1285 assert(std::cmp_equal(in.mapOpGroups.size(), in.dimsPerGroup.size()));
1288 for (Attribute sizeAttr : in.paramsOfStructTy) {
1289 if (AffineMapAttr m = dyn_cast<AffineMapAttr>(sizeAttr)) {
1290 ValueRange currMapOps = in.mapOpGroups[idx++];
1295 SmallVector<OpFoldResult> currMapOpsCast = getAsOpFoldResult(currMapOps);
1297 llvm::dbgs() <<
"[AffineMapFolder] currMapOps as fold results: "
1300 if (
auto constOps = Step4_InstantiateAffineMaps::getConstantIntValues(currMapOpsCast)) {
1301 SmallVector<Attribute> result;
1302 bool hasPoison =
false;
1303 auto constAttrs = llvm::map_to_vector(*constOps, [&rewriter](int64_t v) -> Attribute {
1304 return rewriter.getIndexAttr(v);
1306 LogicalResult foldResult = m.getAffineMap().constantFold(constAttrs, result, &hasPoison);
1311 "Cannot fold affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
1312 " due to divide by 0 or modulus with negative divisor"
1317 if (failed(foldResult)) {
1321 "Folding affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
" failed"
1326 if (result.size() != 1) {
1330 "Folding affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
1331 " produced ", result.size(),
" results but expected 1"
1336 assert(!llvm::isa<AffineMapAttr>(result[0]) &&
"not converted");
1337 out.paramsOfStructTy.push_back(result[0]);
1341 out.mapOpGroups.emplace_back(currMapOps);
1342 out.dimsPerGroup.push_back(in.dimsPerGroup[idx - 1]);
1345 out.paramsOfStructTy.push_back(sizeAttr);
1347 assert(idx == in.mapOpGroups.size() &&
"all affine_map not processed");
1349 in.paramsOfStructTy.size() == out.paramsOfStructTy.size() &&
1350 "produced wrong number of dimensions"
1358class InstantiateAtCreateArrayOp final :
public OpRewritePattern<CreateArrayOp> {
1360 ConversionTracker &tracker_;
1363 InstantiateAtCreateArrayOp(MLIRContext *ctx, ConversionTracker &tracker)
1364 : OpRewritePattern(ctx), tracker_(tracker) {}
1366 LogicalResult matchAndRewrite(
CreateArrayOp op, PatternRewriter &rewriter)
const override {
1369 AffineMapFolder::Output out;
1370 AffineMapFolder::Input in = {
1375 if (failed(AffineMapFolder::fold(rewriter, in, out, op,
"array dimension"))) {
1380 if (newResultType == oldResultType) {
1384 assert(tracker_.isLegalConversion(oldResultType, newResultType,
"InstantiateAtCreateArrayOp"));
1386 llvm::dbgs() <<
"[InstantiateAtCreateArrayOp] instantiating " << oldResultType <<
" as "
1387 << newResultType <<
" in \"" << op <<
"\"\n"
1390 rewriter, op, newResultType, AffineMapFolder::getConvertedMapOpGroups(out), out.dimsPerGroup
1397class InstantiateAtCallOpCompute final :
public OpRewritePattern<CallOp> {
1398 ConversionTracker &tracker_;
1401 InstantiateAtCallOpCompute(MLIRContext *ctx, ConversionTracker &tracker)
1402 : OpRewritePattern(ctx), tracker_(tracker) {}
1404 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
1409 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] target: " << op.
getCallee() <<
'\n');
1411 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] oldRetTy: " << oldRetTy <<
'\n');
1412 ArrayAttr params = oldRetTy.
getParams();
1418 AffineMapFolder::Output out;
1419 AffineMapFolder::Input in = {
1424 if (!in.mapOpGroups.empty()) {
1426 if (failed(AffineMapFolder::fold(rewriter, in, out, op,
"struct parameter"))) {
1430 llvm::dbgs() <<
"[InstantiateAtCallOpCompute] folded affine_map in result type params\n";
1436 if (callArgTypes.empty()) {
1440 if (calleeReferencesTemplateParam(op)) {
1443 SymbolTableCollection tables;
1445 if (failed(lookupRes)) {
1448 if (failed(instantiateViaTargetType(in, out, callArgTypes, lookupRes->get()))) {
1452 llvm::dbgs() <<
"[InstantiateAtCallOpCompute] propagated instantiations via symrefs in "
1453 "result type params: "
1459 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] newRetTy: " << newRetTy <<
'\n');
1460 if (newRetTy == oldRetTy) {
1467 if (!tracker_.isLegalConversion(oldRetTy, newRetTy,
"InstantiateAtCallOpCompute")) {
1468 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1470 "result type mismatch: due to struct instantiation, expected type ", newRetTy,
1471 ", but found ", oldRetTy
1475 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] replaced " << op);
1477 rewriter, op, TypeRange {newRetTy}, op.
getCallee(),
1478 AffineMapFolder::getConvertedMapOpGroups(out), out.dimsPerGroup, op.
getArgOperands()
1481 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
1488 inline LogicalResult instantiateViaTargetType(
1489 const AffineMapFolder::Input &in, AffineMapFolder::Output &out,
1490 OperandRange::type_range callArgTypes,
FuncDefOp targetFunc
1495 assert(in.paramsOfStructTy.size() == targetResTyParams.size());
1497 if (llvm::all_of(in.paramsOfStructTy, isConcreteAttr<>)) {
1503 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
1505 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']' <<
" target func arg types: "
1507 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
1509 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
1516 assert(unifies &&
"should have been checked by verifiers");
1519 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
1528 SmallVector<Attribute> newReturnStructParams = llvm::map_to_vector(
1529 llvm::zip_equal(targetResTyParams.getValue(), in.paramsOfStructTy),
1530 [&unifications](std::tuple<Attribute, Attribute> p) {
1531 Attribute fromCall = std::get<1>(p);
1534 if (!isConcreteAttr(fromCall)) {
1535 Attribute fromTgt = std::get<0>(p);
1537 llvm::dbgs() <<
"[instantiateViaTargetType] fromCall = " << fromCall <<
'\n';
1538 llvm::dbgs() <<
"[instantiateViaTargetType] fromTgt = " << fromTgt <<
'\n';
1540 assert(llvm::isa<SymbolRefAttr>(fromTgt));
1541 auto it = unifications.find(std::make_pair(llvm::cast<SymbolRefAttr>(fromTgt), Side::LHS));
1542 if (it != unifications.end()) {
1543 Attribute unifiedAttr = it->second;
1545 llvm::dbgs() <<
"[instantiateViaTargetType] unifiedAttr = " << unifiedAttr <<
'\n';
1547 if (unifiedAttr && isConcreteAttr<false>(unifiedAttr)) {
1556 out.paramsOfStructTy = std::move(newReturnStructParams);
1557 assert(out.paramsOfStructTy.size() == in.paramsOfStructTy.size() &&
"post-condition");
1558 assert(out.mapOpGroups.empty() &&
"post-condition");
1559 assert(out.dimsPerGroup.empty() &&
"post-condition");
1564LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1565 MLIRContext *ctx = modOp.getContext();
1566 RewritePatternSet patterns(ctx);
1568 InstantiateAtCreateArrayOp,
1569 InstantiateAtCallOpCompute
1572 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
1580class UpdateNewArrayElemFromWrite final :
public OpRewritePattern<CreateArrayOp> {
1581 ConversionTracker &tracker_;
1584 UpdateNewArrayElemFromWrite(MLIRContext *ctx, ConversionTracker &tracker)
1585 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1587 LogicalResult matchAndRewrite(
CreateArrayOp op, PatternRewriter &rewriter)
const override {
1589 ArrayType createResultType = dyn_cast<ArrayType>(createResult.getType());
1590 assert(createResultType &&
"CreateArrayOp must produce ArrayType");
1595 Type newResultElemType =
nullptr;
1596 for (Operation *user : createResult.getUsers()) {
1597 if (
WriteArrayOp writeOp = dyn_cast<WriteArrayOp>(user)) {
1598 if (writeOp.getArrRef() != createResult) {
1601 Type writeRValueType = writeOp.getRvalue().getType();
1602 if (writeRValueType == oldResultElemType) {
1605 if (newResultElemType && newResultElemType != writeRValueType) {
1608 <<
"[UpdateNewArrayElemFromWrite] multiple possible element types for CreateArrayOp "
1609 << newResultElemType <<
" vs " << writeRValueType <<
'\n'
1613 newResultElemType = writeRValueType;
1616 if (!newResultElemType) {
1620 if (!tracker_.isLegalConversion(
1621 oldResultElemType, newResultElemType,
"UpdateNewArrayElemFromWrite"
1626 rewriter.modifyOpInPlace(op, [&createResult, &newType]() { createResult.setType(newType); });
1628 llvm::dbgs() <<
"[UpdateNewArrayElemFromWrite] updated result type of " << op <<
'\n'
1636LogicalResult updateArrayElemFromArrAccessOp(
1638 PatternRewriter &rewriter
1645 if (oldArrType == newArrType ||
1646 !tracker.isLegalConversion(oldArrType, newArrType,
"updateArrayElemFromArrAccessOp")) {
1649 rewriter.modifyOpInPlace(op, [&op, &newArrType]() { op.
getArrRef().setType(newArrType); });
1651 llvm::dbgs() <<
"[updateArrayElemFromArrAccessOp] updated base array type in " << op <<
'\n'
1658class UpdateArrayElemFromArrWrite final :
public OpRewritePattern<WriteArrayOp> {
1659 ConversionTracker &tracker_;
1662 UpdateArrayElemFromArrWrite(MLIRContext *ctx, ConversionTracker &tracker)
1663 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1665 LogicalResult matchAndRewrite(
WriteArrayOp op, PatternRewriter &rewriter)
const override {
1666 return updateArrayElemFromArrAccessOp(op, op.
getRvalue().getType(), tracker_, rewriter);
1670class UpdateArrayElemFromArrRead final :
public OpRewritePattern<ReadArrayOp> {
1671 ConversionTracker &tracker_;
1674 UpdateArrayElemFromArrRead(MLIRContext *ctx, ConversionTracker &tracker)
1675 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1677 LogicalResult matchAndRewrite(
ReadArrayOp op, PatternRewriter &rewriter)
const override {
1678 return updateArrayElemFromArrAccessOp(op, op.
getResult().getType(), tracker_, rewriter);
1685static bool isFullySpecializedMemberType(Type type) {
1690 for (Attribute dim :
array.getDimensionSizes()) {
1691 if (
auto integer = dyn_cast<IntegerAttr>(dim); integer &&
isDynamic(integer)) {
1692 return WalkResult::interrupt();
1695 return WalkResult::advance();
1696 }).wasInterrupted();
1700class UpdateMemberDefTypeFromWrite final :
public OpRewritePattern<MemberDefOp> {
1701 ConversionTracker &tracker_;
1704 UpdateMemberDefTypeFromWrite(MLIRContext *ctx, ConversionTracker &tracker)
1705 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1707 LogicalResult matchAndRewrite(
MemberDefOp op, PatternRewriter &rewriter)
const override {
1709 if (isFullySpecializedMemberType(op.
getType())) {
1715 assert(parentRes &&
"MemberDefOp parent is always StructDefOp");
1719 Type newType =
nullptr;
1721 std::optional<Location> newTypeLoc = std::nullopt;
1722 for (SymbolTable::SymbolUse symUse : memberUsers.value()) {
1723 if (
MemberWriteOp writeOp = llvm::dyn_cast<MemberWriteOp>(symUse.getUser())) {
1724 Type writeToType = writeOp.getVal().getType();
1725 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateMemberDefTypeFromWrite] checking " << writeOp <<
'\n');
1728 newType = writeToType;
1729 newTypeLoc = writeOp.getLoc();
1730 }
else if (writeToType != newType) {
1736 if (!tracker_.isLegalConversion(writeToType, newType,
"UpdateMemberDefTypeFromWrite")) {
1737 if (tracker_.isLegalConversion(
1738 newType, writeToType,
"UpdateMemberDefTypeFromWrite"
1741 newType = writeToType;
1742 newTypeLoc = writeOp.getLoc();
1745 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1749 "' with different value types"
1752 diag.attachNote(newTypeLoc).append(
"type written here is ", newType);
1754 diag.attachNote(writeOp.getLoc()).append(
"type written here is ", writeToType);
1762 if (!newType || newType == op.
getType()) {
1765 if (!tracker_.isLegalConversion(op.
getType(), newType,
"UpdateMemberDefTypeFromWrite")) {
1768 rewriter.modifyOpInPlace(op, [&op, &newType]() { op.
setType(newType); });
1769 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateMemberDefTypeFromWrite] updated type of " << op <<
'\n');
1776SmallVector<std::unique_ptr<Region>> moveRegions(Operation *op) {
1777 SmallVector<std::unique_ptr<Region>> newRegions;
1778 for (Region ®ion : op->getRegions()) {
1779 auto newRegion = std::make_unique<Region>();
1780 newRegion->takeBody(region);
1781 newRegions.push_back(std::move(newRegion));
1790class UpdateInferredResultTypes final :
public OpTraitRewritePattern<OpTrait::InferTypeOpAdaptor> {
1791 ConversionTracker &tracker_;
1794 UpdateInferredResultTypes(MLIRContext *ctx, ConversionTracker &tracker)
1795 : OpTraitRewritePattern(ctx, 6), tracker_(tracker) {}
1797 LogicalResult matchAndRewrite(Operation *op, PatternRewriter &rewriter)
const override {
1798 SmallVector<Type, 1> inferredResultTypes;
1799 InferTypeOpInterface retTypeFn = llvm::cast<InferTypeOpInterface>(op);
1800 LogicalResult result = retTypeFn.inferReturnTypes(
1801 op->getContext(), op->getLoc(), op->getOperands(), op->getRawDictionaryAttrs(),
1802 op->getPropertiesStorage(), op->getRegions(), inferredResultTypes
1804 if (failed(result)) {
1807 if (op->getResultTypes() == inferredResultTypes) {
1810 if (!tracker_.areLegalConversions(
1811 op->getResultTypes(), inferredResultTypes,
"UpdateInferredResultTypes"
1817 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateInferredResultTypes] replaced " << *op);
1818 SmallVector<std::unique_ptr<Region>> newRegions = moveRegions(op);
1819 Operation *newOp = rewriter.create(
1820 op->getLoc(), op->getName().getIdentifier(), op->getOperands(), inferredResultTypes,
1821 op->getAttrs(), op->getSuccessors(), newRegions
1823 rewriter.replaceOp(op, newOp);
1824 LLVM_DEBUG(llvm::dbgs() <<
" with " << *newOp <<
'\n');
1830class UpdateFuncTypeFromReturn final :
public OpRewritePattern<FuncDefOp> {
1831 ConversionTracker &tracker_;
1834 UpdateFuncTypeFromReturn(MLIRContext *ctx, ConversionTracker &tracker)
1835 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1837 LogicalResult matchAndRewrite(
FuncDefOp op, PatternRewriter &rewriter)
const override {
1838 Region &body = op.getFunctionBody();
1842 ReturnOp retOp = llvm::dyn_cast<ReturnOp>(body.back().getTerminator());
1843 assert(retOp &&
"final op in body region must be return");
1844 OperandRange::type_range tyFromReturnOp = retOp.
getOperands().getTypes();
1847 if (oldFuncTy.getResults() == tyFromReturnOp) {
1850 if (!tracker_.areLegalConversions(
1851 oldFuncTy.getResults(), tyFromReturnOp,
"UpdateFuncTypeFromReturn"
1856 rewriter.modifyOpInPlace(op, [&]() {
1857 op.
setFunctionType(rewriter.getFunctionType(oldFuncTy.getInputs(), tyFromReturnOp));
1860 llvm::dbgs() <<
"[UpdateFuncTypeFromReturn] changed " << op.
getSymName() <<
" from "
1871class UpdateFreeFuncCallOpTypes final :
public OpRewritePattern<CallOp> {
1872 ConversionTracker &tracker_;
1875 UpdateFreeFuncCallOpTypes(MLIRContext *ctx, ConversionTracker &tracker)
1876 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1878 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
1879 if (calleeReferencesTemplateParam(op)) {
1882 SymbolTableCollection tables;
1884 if (failed(lookupRes)) {
1887 FuncDefOp targetFunc = lookupRes->get();
1892 if (op.getResultTypes() == targetFunc.
getFunctionType().getResults()) {
1895 if (!tracker_.areLegalConversions(
1897 "UpdateFreeFuncCallOpTypes"
1902 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateFreeFuncCallOpTypes] replaced " << op);
1905 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
1912LogicalResult updateMemberRefValFromMemberDef(
1915 SymbolTableCollection tables;
1920 Type oldResultType = op.
getVal().getType();
1921 Type newResultType = def->get().getType();
1922 if (oldResultType == newResultType ||
1923 !tracker.isLegalConversion(oldResultType, newResultType,
"updateMemberRefValFromMemberDef")) {
1926 rewriter.modifyOpInPlace(op, [&op, &newResultType]() { op.
getVal().setType(newResultType); });
1928 llvm::dbgs() <<
"[updateMemberRefValFromMemberDef] updated value type in " << op <<
'\n'
1936class UpdateMemberReadValFromDef final :
public OpRewritePattern<MemberReadOp> {
1937 ConversionTracker &tracker_;
1940 UpdateMemberReadValFromDef(MLIRContext *ctx, ConversionTracker &tracker)
1941 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1943 LogicalResult matchAndRewrite(
MemberReadOp op, PatternRewriter &rewriter)
const override {
1944 return updateMemberRefValFromMemberDef(op, tracker_, rewriter);
1949class UpdateMemberWriteValFromDef final :
public OpRewritePattern<MemberWriteOp> {
1950 ConversionTracker &tracker_;
1953 UpdateMemberWriteValFromDef(MLIRContext *ctx, ConversionTracker &tracker)
1954 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
1956 LogicalResult matchAndRewrite(
MemberWriteOp op, PatternRewriter &rewriter)
const override {
1957 return updateMemberRefValFromMemberDef(op, tracker_, rewriter);
1961LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1962 MLIRContext *ctx = modOp.getContext();
1963 RewritePatternSet patterns(ctx);
1968 UpdateInferredResultTypes,
1970 UpdateFreeFuncCallOpTypes,
1971 UpdateFuncTypeFromReturn,
1972 UpdateNewArrayElemFromWrite,
1973 UpdateArrayElemFromArrRead,
1974 UpdateArrayElemFromArrWrite,
1975 UpdateMemberDefTypeFromWrite,
1976 UpdateMemberReadValFromDef,
1977 UpdateMemberWriteValFromDef
1980 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
1991 static bool hasTemplateSymbolBindings(Operation *op) {
1992 if (
StructDefOp sdef = llvm::dyn_cast<StructDefOp>(op)) {
1993 return sdef.hasTemplateSymbolBindings();
1995 if (llvm::isa<function::FuncDefOp>(op)) {
2006 LogicalResult eraseUnreachableFrom(ArrayRef<SymbolOpInterface> keep) {
2008 SetVector<SymbolOpInterface> roots(keep.begin(), keep.end());
2015 DenseSet<Operation *> defsToKeep;
2016 llvm::df_iterator_default_set<const SymbolUseGraphNode *> symbolsToKeep;
2017 for (
size_t i = 0; i < roots.size(); ++i) {
2018 SymbolOpInterface keepRoot = roots[i];
2019 LLVM_DEBUG({ llvm::dbgs() <<
"[EraseUnreachable] root: " << keepRoot <<
'\n'; });
2021 assert(keepRootNode &&
"every symbol def must be in the def tree");
2022 for (
const SymbolDefTreeNode *reachableDefNode : llvm::depth_first(keepRootNode)) {
2024 llvm::dbgs() <<
"[EraseUnreachable] can reach: " << reachableDefNode->getOp() <<
'\n';
2026 if (SymbolOpInterface reachableDef = reachableDefNode->getOp()) {
2028 defsToKeep.insert(reachableDef.getOperation());
2034 if (
const SymbolUseGraphNode *useGraphNodeForDef = useGraph.lookupNode(reachableDef)) {
2036 depth_first_ext(useGraphNodeForDef, symbolsToKeep)) {
2038 llvm::dbgs() <<
"[EraseUnreachable] uses symbol: "
2039 << usedSymbolNode->getSymbolPath() <<
'\n';
2043 if (usedSymbolNode->isTemplateSymbolBinding()) {
2048 auto lookupRes = usedSymbolNode->lookupSymbol(tables);
2049 if (failed(lookupRes)) {
2050 LLVM_DEBUG(useGraph.dumpToDotFile());
2054 if (lookupRes->viaInclude()) {
2057 Operation *usedOp = lookupRes->get();
2059 SymbolOpInterface asSymbol = llvm::cast<SymbolOpInterface>(usedOp);
2060 bool insertRes = roots.insert(asSymbol);
2064 llvm::dbgs() <<
"[EraseUnreachable] found another root: " << asSymbol <<
'\n';
2074 SmallVector<SymbolOpInterface> toErase;
2075 rootMod.walk([
this, &defsToKeep, &symbolsToKeep, &toErase](Operation *op) {
2079 SymbolOpInterface symOp = llvm::cast<SymbolOpInterface>(op);
2081 if (!n || !symbolsToKeep.contains(n)) {
2082 LLVM_DEBUG(llvm::dbgs() <<
"[EraseUnreachable] removing: " << symOp.getNameAttr() <<
'\n');
2083 toErase.push_back(symOp);
2086 for (SymbolOpInterface symOp : toErase) {
2097 using Base = FlatteningPassBase<PassImpl>;
2106 void runOnOperation()
override {
2107 ModuleOp modOp = getOperation();
2108 if (failed(runOn(modOp))) {
2111 llvm::dbgs() <<
"=====================================================================\n";
2112 llvm::dbgs() <<
" Dumping module after failure of pass " <<
DEBUG_TYPE <<
'\n';
2113 modOp.print(llvm::dbgs(), OpPrintingFlags().assumeVerified());
2114 llvm::dbgs() <<
"=====================================================================\n";
2116 signalPassFailure();
2120 inline LogicalResult runOn(ModuleOp modOp) {
2126 if (effectiveCleanupMode == FlatteningCleanupMode::MainAsRoot) {
2127 if (failed(eraseUnreachableFromMainStruct(modOp))) {
2135 OpPassManager universalCleanup(ModuleOp::getOperationName());
2140 if (failed(runPipeline(universalCleanup, modOp))) {
2144 ConversionTracker tracker;
2145 if (failed(Step1_InstantiateStructs::instantiateMainStruct(modOp, tracker))) {
2146 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating the main struct\n";
2150 unsigned loopCount = 0;
2153 if (loopCount > iterationLimit) {
2154 llvm::errs() <<
DEBUG_TYPE <<
" exceeded the limit of " << iterationLimit
2155 <<
" iterations!\n";
2158 tracker.resetModifiedFlag();
2161 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2162 <<
")] Running step 1: struct instantiation\n";
2167 if (failed(Step1_InstantiateStructs::run(modOp, tracker))) {
2168 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating structs in templates\n";
2172 if (failed(Step2_InstantiateFunctions::run(modOp, tracker))) {
2173 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating functions in templates\n";
2178 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2179 <<
")] Running step 2: loop unrolling\n";
2182 if (failed(Step3_Unroll::run(modOp, tracker))) {
2183 llvm::errs() <<
DEBUG_TYPE <<
" failed while unrolling loops\n";
2188 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2189 <<
")] Running step 3: affine maps instantiation\n";
2192 if (failed(Step4_InstantiateAffineMaps::run(modOp, tracker))) {
2193 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating `affine_map` parameters\n";
2198 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2199 <<
")] Running step 4: type propagation\n";
2202 if (failed(Step5_PropagateTypes::run(modOp, tracker))) {
2203 llvm::errs() <<
DEBUG_TYPE <<
" failed while propagating instantiated types\n";
2207 LLVM_DEBUG(
if (tracker.isModified()) {
2208 llvm::dbgs() <<
"=====================================================================\n";
2209 llvm::dbgs() <<
" Dumping module between iterations of " << DEBUG_TYPE <<
'\n';
2210 modOp.print(llvm::dbgs(), OpPrintingFlags().assumeVerified());
2211 llvm::dbgs() <<
"=====================================================================\n";
2213 }
while (tracker.isModified());
2215 tracker.clearPartialFuncInstantiations();
2218 if (failed(cleanupSwitch(modOp, tracker))) {
2222 if (failed(runPipeline(universalCleanup, modOp))) {
2226 OpPassManager allocationCleanup(ModuleOp::getOperationName());
2228 RemoveUnusedDiscardableAllocationsPassOptions {
2232 return runPipeline(allocationCleanup, modOp);
2236 LogicalResult cleanupSwitch(ModuleOp modOp,
const ConversionTracker &tracker) {
2238 LLVM_DEBUG({ llvm::dbgs() <<
"[FlatteningPass] Running step 5: cleanup "; });
2239 switch (effectiveCleanupMode) {
2240 case FlatteningCleanupMode::MainAsRoot:
2241 LLVM_DEBUG(llvm::dbgs() <<
"(main as root mode)\n");
2242 return eraseUnreachableFromMainStruct(modOp,
false);
2243 case FlatteningCleanupMode::ConcreteAsRoot:
2244 LLVM_DEBUG(llvm::dbgs() <<
"(concrete definitions mode)\n");
2245 return eraseUnreachableFromConcreteDefinitions(modOp);
2246 case FlatteningCleanupMode::Preimage:
2247 LLVM_DEBUG(llvm::dbgs() <<
"(preimage mode)\n");
2248 return erasePreimageOfInstantiations(modOp, tracker);
2249 case FlatteningCleanupMode::Unspecified:
2251 LLVM_DEBUG(llvm::dbgs() <<
"(disabled)\n");
2257 LogicalResult erasePreimageOfInstantiations(ModuleOp rootMod,
const ConversionTracker &tracker) {
2262 FromEraseSet cleaner(
2263 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>(),
2264 tracker.getInstantiatedDefinitionNames()
2266 LogicalResult res = cleaner.eraseUnusedDefinitions();
2267 if (succeeded(res)) {
2268 LLVM_DEBUG(llvm::dbgs() <<
"[Cleanup(preimage)] success\n";);
2270 const SymbolUseGraph *useGraph =
nullptr;
2271 rootMod->walk([
this, &cleaner, &useGraph](Operation *walkedOp) {
2272 SymbolOpInterface op = llvm::dyn_cast<SymbolOpInterface>(walkedOp);
2273 if (!op || !cleaner.getTryToEraseSet().contains(op)) {
2278 useGraph = &getAnalysis<SymbolUseGraph>();
2281 if (useGraph->lookupNode(op)->hasPredecessor()) {
2282 op.emitWarning(
"Parameterized definition still has uses!").report();
2286 LLVM_DEBUG(llvm::dbgs() <<
"[Cleanup(preimage)] failed\n";);
2291 LogicalResult eraseUnreachableFromConcreteDefinitions(ModuleOp rootMod) {
2292 SmallVector<SymbolOpInterface> roots;
2293 rootMod.walk([&roots](Operation *op) {
2295 roots.push_back(llvm::cast<SymbolOpInterface>(op));
2299 Step6_Cleanup::FromKeepSet cleaner(
2300 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>()
2302 return cleaner.eraseUnreachableFrom(roots);
2305 LogicalResult eraseUnreachableFromMainStruct(ModuleOp rootMod,
bool emitWarning =
true) {
2306 Step6_Cleanup::FromKeepSet cleaner(
2307 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>()
2309 FailureOr<SymbolLookupResult<StructDefOp>> mainOpt =
2311 if (failed(mainOpt)) {
2314 SymbolLookupResult<StructDefOp>
main = mainOpt.value();
2315 if (emitWarning && !
main) {
2318 rootMod.emitWarning()
2320 "using option '", cleanupMode.getArgStr(),
'=',
2323 "\" attribute on the top-level module may remove all cleanup-candidate definitions!"
2327 SmallVector<SymbolOpInterface> roots;
2329 roots.push_back(*
main);
2331 return cleaner.eraseUnreachableFrom(roots);
Common private implementation for poly dialect passes.
This file defines methods symbol lookup across LLZK operations and included files.
Private parameter substitution helpers shared by polymorphic transformations.
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
Gets the SSA Value for the referenced array.
inline ::llzk::array::ArrayType getArrRefType()
Gets the type of 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
static ArrayType get(::mlir::Type elementType, ::llvm::ArrayRef<::mlir::Attribute > dimensionSizes)
::llvm::ArrayRef<::mlir::Attribute > getDimensionSizes() const
::mlir::TypedValue<::llzk::array::ArrayType > getResult()
::mlir::DenseI32ArrayAttr getNumDimsPerMapAttr()
static constexpr ::llvm::StringLiteral getOperationName()
::mlir::OperandRangeRange getMapOperands()
::mlir::TypedValue<::mlir::Type > getResult()
::mlir::TypedValue<::mlir::Type > getRvalue()
static constexpr ::llvm::StringLiteral getOperationName()
void setType(::mlir::Type attrValue)
::mlir::Value getVal()
Gets the SSA Value that holds the read/write data for the MemberRefOp.
::mlir::FailureOr< SymbolLookupResult< MemberDefOp > > getMemberDefOp(::mlir::SymbolTableCollection &tables)
Gets the definition for the member referenced in this op.
static constexpr ::llvm::StringLiteral getOperationName()
StructType getType(::std::optional<::mlir::ArrayAttr > constParams={})
Gets the StructType representing this struct.
::llvm::StringRef getSymName()
void setSymName(::llvm::StringRef attrValue)
::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
bool calleeIsStructConstrain()
Return true iff the callee function name is FUNC_NAME_CONSTRAIN within a StructDefOp.
::llzk::component::StructType getSingleResultTypeOfCompute()
Assuming the callee is FUNC_NAME_COMPUTE, return the single StructType result.
::mlir::SymbolRefAttr getCalleeAttr()
::llvm::LogicalResult verifySymbolUses(::mlir::SymbolTableCollection &symbolTable)
bool calleeIsStructCompute()
Return true iff the callee function name is FUNC_NAME_COMPUTE within a StructDefOp.
::mlir::SymbolRefAttr getCallee()
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
::mlir::Operation::operand_range getArgOperands()
::mlir::ArrayAttr getTemplateParamsAttr()
::mlir::OperandRangeRange getMapOperands()
void setCalleeAttr(::mlir::SymbolRefAttr attr)
::mlir::FailureOr<::llzk::SymbolLookupResult<::llzk::function::FuncDefOp > > getCalleeTarget(::mlir::SymbolTableCollection &tables)
Resolve and return the target FuncDefOp for this CallOp.
::mlir::DenseI32ArrayAttr getNumDimsPerMapAttr()
FuncDefOp clone(::mlir::IRMapping &mapper)
Create a deep copy of this function and all of its blocks, remapping any operands that use values out...
::mlir::FunctionType getFunctionType()
::llvm::ArrayRef<::mlir::Type > getArgumentTypes()
Required by FunctionOpInterface.
::llzk::component::StructType getSingleResultTypeOfCompute()
Assuming the name is FUNC_NAME_COMPUTE, return the single StructType result.
::llvm::StringRef getSymName()
bool isStructCompute()
Return true iff the function is within a StructDefOp and named FUNC_NAME_COMPUTE.
bool isInStruct()
Return true iff the function is within a StructDefOp.
void setFunctionType(::mlir::FunctionType attrValue)
void setSymName(::llvm::StringRef attrValue)
::mlir::StringAttr getSymNameAttr()
::mlir::Operation::operand_range getOperands()
::mlir::Region & getBodyRegion()
bool hasConstNamed(::mlir::StringRef find)
Return true if there is an op of type OpT with the given name within the body region.
::mlir::StringAttr getSymNameAttr()
void setSymName(::llvm::StringRef attrValue)
::llvm::StringRef getSymName()
inline ::llvm::iterator_range<::mlir::Region::op_iterator< OpT > > getConstOps()
Return ops of type OpT within the body region.
::mlir::StringAttr getSymNameAttr()
Shared state for post-instantiation cleanup helpers.
CleanupBase(mlir::ModuleOp root, const SymbolDefTree &symDefTree, const SymbolUseGraph &symUseGraph)
Substitute known template bindings in function types and inference candidates.
mlir::Attribute convertAttr(mlir::Attribute attr) const
Substitute a bound attribute or the type contained in a TypeAttr.
int main(int argc, char **argv)
std::string toStringList(InputIt begin, InputIt end)
Generate a comma-separated string representation by traversing elements from begin to end where the e...
Common interface for constant parameter-like symbol operations that can be defined within a TemplateO...
mlir::ConversionTarget newConverterDefinedTargetWithCallback(mlir::TypeConverter &tyConv, mlir::MLIRContext *ctx, LegalityCheckCallback &cb, AdditionalChecks &&...checks)
Return a new ConversionTarget allowing all LLZK-required dialects and defining Op legality based on t...
std::optional< Attribute > inferUnifiedParam(const UnificationMap &unifyResult, SymbolRefAttr paramName)
Return the callee-side unification-derived value for a template parameter, if any.
FailureOr< InstantiationLayout > buildInstantiationLayout(TemplateOp parentTemplate, ArrayAttr callParams, const DenseMap< Attribute, Attribute > ¶mNameToConcrete)
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)
void evaluateTemplateExprs(TemplateOp templateOp, DenseMap< Attribute, Attribute > ¶mNameToConcrete)
Evaluate all TemplateExprOps in templateOp that can be computed from the currently-known concrete par...
void convertCalleesInPlace(Operation *op, const DenseMap< Attribute, Attribute > ¶mNameToValue)
void setInstantiationNamePattern(TemplateOp templateOp, ArrayAttr namePattern)
bool isErasableDefinition(mlir::Operation *op)
Return true iff op is a cleanup candidate.
void reportDelayedDiagnostics(Operation *site, SmallVector< Diagnostic > &&diagnostics)
std::unique_ptr<::mlir::Pass > createEmptyTemplateRemovalPass()
::llvm::StringRef stringifyFlatteningCleanupMode(FlatteningCleanupMode val)
OpClass replaceOpWithNewOp(Rewriter &rewriter, mlir::Operation *op, Args &&...args)
Wrapper for RewriterBase::replaceOpWithNewOp() that automatically copies discardable attributes (i....
std::unique_ptr<::mlir::Pass > createRemoveUnusedDiscardableAllocationsPass()
bool typeListsUnify(Iter1 lhs, Iter2 rhs, mlir::ArrayRef< llvm::StringRef > rhsReversePrefix={}, UnificationMap *unifications=nullptr)
Return true iff the two lists of Type instances are equivalent or could be equivalent after full inst...
bool isConcreteType(Type type, bool allowStructParams)
mlir::FailureOr< SymbolLookupResultUntyped > lookupTopLevelSymbol(mlir::SymbolTableCollection &tables, mlir::SymbolRefAttr symbol, mlir::Operation *origin, bool reportMissing=true)
FailureOr< StructType > getMainInstanceType(Operation *lookupFrom)
std::optional< mlir::SymbolTable::UseRange > getSymbolUses(mlir::Operation *from)
Get an iterator range for all of the uses, for any symbol, that are nested within the given operation...
TypeClass getIfSingleton(mlir::TypeRange types)
AttrConcreteness
Concreteness classification for an argument to a parameterized struct type.
mlir::DenseMap< std::pair< mlir::SymbolRefAttr, Side >, mlir::Attribute > UnificationMap
Optional result from type unifications.
bool isNullOrEmpty(mlir::ArrayAttr a)
SymbolRefAttr appendLeaf(SymbolRefAttr orig, FlatSymbolRefAttr newLeaf)
OpClass getParentOfType(mlir::Operation *op)
Return the closest surrounding parent/ancestor operation that is of type 'OpClass'.
AttrConcreteness classifyAttrConcreteness(Attribute attr, bool allowStructParams)
TypeClass getAtIndex(mlir::TypeRange types, size_t index)
mlir::RewritePatternSet newGeneralRewritePatternSet(mlir::TypeConverter &tyConv, mlir::MLIRContext *ctx, mlir::ConversionTarget &target)
Return a new RewritePatternSet covering all LLZK op types that may contain a StructType.
bool isDynamic(IntegerAttr intAttr)
mlir::SymbolRefAttr asSymbolRefAttr(mlir::StringAttr root, mlir::SymbolRefAttr tail)
Build a SymbolRefAttr that prepends tail with root, i.e., root::tail.
FailureOr< SymbolLookupResult< StructDefOp > > getMainInstanceDef(SymbolTableCollection &symbolTable, Operation *lookupFrom)
bool isMoreConcreteUnification(Type oldTy, Type newTy, llvm::function_ref< bool(Type oldTy, Type newTy)> knownOldToNew)
llvm::SmallVector< FlatSymbolRefAttr > getPieces(SymbolRefAttr ref)
constexpr char MAIN_ATTR_NAME[]
Name of the attribute on the top-level ModuleOp that specifies the type of the main struct.
Groups the information needed after concrete parameters have been chosen to decide how to name a new ...
mlir::ArrayAttr namePattern
The refined literal chunks; fully concrete generated templates clear this state.
std::string templateNameWithAttrs
mlir::ArrayAttr rewrittenCallParams
mlir::SmallVector< mlir::Attribute > remainingNames
mlir::ArrayAttr concreteParamKey
Ordered [parameter-name, concrete-value, ...] entries for exact partial-function reuse.