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
72#define DEBUG_TYPE "llzk-flatten"
86static void reportDelayedDiagnostics(
CallOp caller, SmallVector<Diagnostic> &&diagnostics) {
87 DiagnosticEngine &engine = caller.getContext()->getDiagEngine();
88 for (Diagnostic &diag : diagnostics) {
90 for (Diagnostic ¬e : diag.getNotes()) {
91 assert(note.getNotes().empty() &&
"notes cannot have notes attached");
92 if (llvm::isa<UnknownLoc>(note.getLocation())) {
93 note = std::move(Diagnostic(caller.getLoc(), note.getSeverity()).append(note.str()));
97 engine.emit(std::move(diag));
101class ConversionTracker {
106 struct PartialFuncInstantiation {
107 ArrayAttr concreteParamKey;
108 StringAttr templateName;
109 StringAttr functionName;
116 DenseMap<StructType, StructType> structInstantiations;
118 DenseMap<StructType, StructType> reverseInstantiations;
120 DenseSet<SymbolRefAttr> funcInstantiations;
123 DenseMap<Operation *, SmallVector<PartialFuncInstantiation>> partialFuncInstantiations;
126 DenseMap<StructType, SmallVector<Diagnostic>> delayedDiagnostics;
129 bool isModified()
const {
return modified; }
130 void resetModifiedFlag() { modified =
false; }
131 void updateModifiedFlag(
bool currStepModified) { modified |= currStepModified; }
136 auto forwardResult = structInstantiations.try_emplace(oldType, newType);
137 if (forwardResult.second) {
140 assert(!reverseInstantiations.contains(newType));
141 reverseInstantiations[newType] = oldType;
146 assert(forwardResult.first->getSecond() == newType);
148 assert(reverseInstantiations.lookup(newType) == oldType);
150 assert(structInstantiations.size() == reverseInstantiations.size());
154 std::optional<StructType> getInstantiation(
StructType oldType)
const {
155 auto cachedResult = structInstantiations.find(oldType);
156 if (cachedResult != structInstantiations.end()) {
157 return cachedResult->second;
163 void recordInstantiation(SymbolRefAttr funcName) {
164 funcInstantiations.insert(funcName);
169 std::optional<SymbolRefAttr>
170 lookupPartialFuncInstantiation(
FuncDefOp sourceFunc, ArrayAttr concreteParamKey)
const {
171 auto found = partialFuncInstantiations.find(sourceFunc.getOperation());
172 if (found == partialFuncInstantiations.end()) {
175 for (
const PartialFuncInstantiation &candidate : found->second) {
176 if (candidate.concreteParamKey == concreteParamKey) {
177 SmallVector<FlatSymbolRefAttr> calleeSuffix {
178 FlatSymbolRefAttr::get(candidate.templateName),
179 FlatSymbolRefAttr::get(candidate.functionName),
188 void recordPartialFuncInstantiation(
192 !lookupPartialFuncInstantiation(sourceFunc, concreteParamKey).has_value() &&
193 "partial function instantiation already cached"
195 partialFuncInstantiations[sourceFunc.getOperation()].push_back(
196 PartialFuncInstantiation {
206 void clearPartialFuncInstantiations() { partialFuncInstantiations.clear(); }
209 DenseSet<SymbolRefAttr> getInstantiatedDefinitionNames()
const {
210 DenseSet<SymbolRefAttr> instantiatedNames = funcInstantiations;
211 for (
const auto &[origRemoteTy, _] : structInstantiations) {
212 instantiatedNames.insert(origRemoteTy.getNameRef());
214 return instantiatedNames;
218 auto res = delayedDiagnostics.find(newType);
219 if (res != delayedDiagnostics.end()) {
220 ::reportDelayedDiagnostics(caller, std::move(res->second));
225 delayedDiagnostics.erase(newType);
229 SmallVector<Diagnostic> &delayedDiagnosticSet(
StructType newType) {
230 return delayedDiagnostics[newType];
235 bool isLegalConversion(Type oldType, Type newType,
const char *patName)
const {
236 std::function<bool(Type, Type)> checkInstantiations = [&](Type oTy, Type nTy) {
238 if (StructType oldStructType = llvm::dyn_cast<StructType>(oTy)) {
241 if (this->structInstantiations.lookup(oldStructType) == nTy) {
248 if (StructType newStructType = llvm::dyn_cast<StructType>(nTy)) {
249 if (
auto preImage = this->reverseInstantiations.lookup(newStructType)) {
262 llvm::dbgs() <<
"[" << patName <<
"] Cannot replace old type " << oldType
263 <<
" with new type " << newType
264 <<
" because it does not define a compatible and more concrete type.\n";
269 template <
typename T,
typename U>
270 inline bool areLegalConversions(T oldTypes, U newTypes,
const char *patName)
const {
272 llvm::zip_equal(oldTypes, newTypes), [
this, &patName](std::tuple<Type, Type> oldThenNew) {
273 return this->isLegalConversion(std::get<0>(oldThenNew), std::get<1>(oldThenNew), patName);
279template <
typename Impl,
typename Op,
typename... HandledAttrs>
280class SymbolUserHelper :
public OpConversionPattern<Op> {
282 const DenseMap<Attribute, Attribute> ¶mNameToValue;
285 TypeConverter &converter, MLIRContext *ctx,
unsigned patternBenefit,
286 const DenseMap<Attribute, Attribute> ¶mNameToInstantiatedValue
288 : OpConversionPattern<Op>(converter, ctx, patternBenefit),
289 paramNameToValue(paramNameToInstantiatedValue) {}
292 using OpAdaptor =
typename mlir::OpConversionPattern<Op>::OpAdaptor;
294 virtual Attribute getNameAttr(Op)
const = 0;
296 virtual LogicalResult handleDefaultRewrite(
297 Attribute, Op op, OpAdaptor, ConversionPatternRewriter &, Attribute a
299 return op->emitOpError().append(
"expected value with type ", op.getType(),
" but found ", a);
303 matchAndRewrite(Op op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter)
const override {
304 LLVM_DEBUG(llvm::dbgs() <<
"[SymbolUserHelper] op: " << op <<
'\n');
305 auto res = this->paramNameToValue.find(getNameAttr(op));
306 if (res == this->paramNameToValue.end()) {
307 LLVM_DEBUG(llvm::dbgs() <<
"[SymbolUserHelper] no instantiation for " << op <<
'\n');
310 llvm::TypeSwitch<Attribute, LogicalResult> TS(res->second);
311 llvm::TypeSwitch<Attribute, LogicalResult> *ptr = &TS;
313 ((ptr = &(ptr->template Case<HandledAttrs>([&](HandledAttrs a) {
314 return static_cast<const Impl *
>(
this)->handleRewrite(res->first, op, adaptor, rewriter, a);
318 return TS.Default([&](Attribute a) {
319 return handleDefaultRewrite(res->first, op, adaptor, rewriter, a);
325class ClonedBodyConstReadOpPattern
326 :
public SymbolUserHelper<
327 ClonedBodyConstReadOpPattern, ConstReadOp, IntegerAttr, FeltConstAttr> {
328 SmallVector<Diagnostic> &diagnostics;
331 SymbolUserHelper<ClonedBodyConstReadOpPattern, ConstReadOp, IntegerAttr, FeltConstAttr>;
334 ClonedBodyConstReadOpPattern(
335 TypeConverter &converter, MLIRContext *ctx,
336 const DenseMap<Attribute, Attribute> ¶mNameToInstantiatedValue,
337 SmallVector<Diagnostic> &instantiationDiagnostics
340 : super(converter, ctx, 1, paramNameToInstantiatedValue),
341 diagnostics(instantiationDiagnostics) {}
343 Attribute getNameAttr(ConstReadOp op)
const override {
return op.
getConstNameAttr(); }
345 LogicalResult handleRewrite(
346 Attribute sym, ConstReadOp op, OpAdaptor, ConversionPatternRewriter &rewriter, IntegerAttr a
348 APInt attrValue = a.getValue();
349 Type origResTy = op.getType();
350 Type newResTy = getTypeConverter()->convertType(origResTy);
352 return op->emitOpError().append(
"could not convert result type ", origResTy);
355 if (FeltType ty = llvm::dyn_cast<FeltType>(newResTy)) {
357 rewriter, op, FeltConstAttr::get(getContext(), attrValue, ty)
362 if (llvm::isa<IndexType>(newResTy)) {
367 if (newResTy.isSignlessInteger(1)) {
369 if (attrValue.isZero()) {
373 if (!attrValue.isOne()) {
374 Location opLoc = op.getLoc();
375 Diagnostic diag(opLoc, DiagnosticSeverity::Warning);
377 if (getContext()->shouldPrintOpOnDiagnostic()) {
378 diag.attachNote(opLoc) <<
"see current operation: " << *op;
380 diag.attachNote(UnknownLoc::get(getContext()))
382 <<
"\" for this call";
383 diagnostics.push_back(std::move(diag));
388 return op->emitOpError().append(
"unexpected result type ", newResTy);
391 LogicalResult handleRewrite(
392 Attribute, ConstReadOp op, OpAdaptor, ConversionPatternRewriter &rewriter, FeltConstAttr a
401struct MatchFailureListener :
public RewriterBase::Listener {
402 bool hadFailure =
false;
404 ~MatchFailureListener()
override {}
406 void notifyMatchFailure(Location loc, function_ref<
void(Diagnostic &)> reasonCallback)
override {
409 InFlightDiagnostic diag = emitError(loc);
410 reasonCallback(*diag.getUnderlyingDiagnostic());
416applyAndFoldGreedily(ModuleOp modOp, ConversionTracker &tracker, RewritePatternSet &&patterns) {
417 bool currStepModified =
false;
418 MatchFailureListener failureListener;
419 LogicalResult result = applyPatternsGreedily(
420 modOp->getRegion(0), std::move(patterns),
421 GreedyRewriteConfig {.maxIterations = 20, .listener = &failureListener, .fold = true},
424 tracker.updateModifiedFlag(currStepModified);
425 return failure(result.failed() || failureListener.hadFailure);
429template <
bool AllowStructParams = true>
bool isConcreteAttr(Attribute a) {
434convertCalleeSymRefs(SymbolRefAttr callee,
const DenseMap<Attribute, Attribute> ¶mNameToValue) {
435 auto it = paramNameToValue.find(FlatSymbolRefAttr::get(callee.getRootReference()));
436 if (it == paramNameToValue.end()) {
440 auto tyAttr = llvm::dyn_cast<TypeAttr>(it->second);
445 auto structTy = llvm::dyn_cast<StructType>(tyAttr.getValue());
450 SmallVector<FlatSymbolRefAttr> newPieces =
getPieces(structTy.getNameRef());
451 llvm::append_range(newPieces, callee.getNestedReferences());
456convertCalleesInPlace(Operation *op,
const DenseMap<Attribute, Attribute> ¶mNameToValue) {
457 op->walk([¶mNameToValue](
CallOp callOp) {
462static bool calleeReferencesTemplateParam(
CallOp op) {
464 if (!callee || callee.getNestedReferences().size() != 1) {
468 if (!parentTemplate) {
478static std::optional<Attribute>
479evaluateExpr(
TemplateExprOp exprOp,
const DenseMap<Attribute, Attribute> ¶mNameToConcrete) {
481 DenseMap<Value, Attribute> valueMap;
483 if (
auto yieldOp = llvm::dyn_cast<YieldOp>(bodyOp)) {
484 auto it = valueMap.find(yieldOp.getVal());
485 return it != valueMap.end() ? std::make_optional(it->second) : std::nullopt;
488 if (
auto constReadOp = llvm::dyn_cast<ConstReadOp>(bodyOp)) {
489 auto it = paramNameToConcrete.find(constReadOp.getConstNameAttr());
490 if (it == paramNameToConcrete.end()) {
495 Attribute val = it->second;
496 if (
auto intAttr = llvm::dyn_cast<IntegerAttr>(val)) {
497 if (
auto feltTy = llvm::dyn_cast<FeltType>(constReadOp.getResult().getType())) {
498 val = FeltConstAttr::get(bodyOp.getContext(), intAttr.getValue(), feltTy);
501 valueMap[constReadOp.getResult()] = val;
506 SmallVector<Attribute> operandAttrs;
507 operandAttrs.reserve(bodyOp.getNumOperands());
508 for (Value operand : bodyOp.getOperands()) {
509 auto it = valueMap.find(operand);
510 if (it == valueMap.end()) {
513 operandAttrs.push_back(it->second);
517 SmallVector<OpFoldResult> foldResults;
518 if (succeeded(bodyOp.fold(operandAttrs, foldResults)) &&
519 foldResults.size() == bodyOp.getNumResults()) {
520 for (
auto [result, fr] : llvm::zip_equal(bodyOp.getResults(), foldResults)) {
521 if (Attribute a = llvm::dyn_cast<Attribute>(fr)) {
522 valueMap[result] = a;
536evaluateTemplateExprs(
TemplateOp templateOp, DenseMap<Attribute, Attribute> ¶mNameToConcrete) {
538 llvm::dbgs() <<
"[evaluateTemplateExprs] before: " <<
debug::toStringList(paramNameToConcrete)
542 std::optional<Attribute> result = evaluateExpr(exprOp, paramNameToConcrete);
543 if (result.has_value()) {
544 auto exprNameAttr = FlatSymbolRefAttr::get(exprOp.
getSymNameAttr());
545 paramNameToConcrete.try_emplace(exprNameAttr, *result);
547 llvm::dbgs() <<
"[evaluateTemplateExprs] expr @" << exprOp.
getSymName()
548 <<
" evaluated to " << *result <<
'\n'
553 llvm::dbgs() <<
"[evaluateTemplateExprs] after: " <<
debug::toStringList(paramNameToConcrete)
558static inline bool tableOffsetIsntSymbol(
MemberReadOp op) {
559 return !llvm::isa_and_present<SymbolRefAttr>(op.
getTableOffset().value_or(
nullptr));
564class ClonedMemberReadOpPattern
565 :
public SymbolUserHelper<ClonedMemberReadOpPattern, MemberReadOp, IntegerAttr> {
566 using super = SymbolUserHelper<ClonedMemberReadOpPattern, MemberReadOp, IntegerAttr>;
569 ClonedMemberReadOpPattern(
570 TypeConverter &converter, MLIRContext *ctx,
571 const DenseMap<Attribute, Attribute> ¶mNameToInstantiatedValue
574 : super(converter, ctx, 1, paramNameToInstantiatedValue) {}
576 Attribute getNameAttr(MemberReadOp op)
const override {
580 LogicalResult handleRewrite(
581 Attribute, MemberReadOp op, OpAdaptor, ConversionPatternRewriter &rewriter, IntegerAttr a
583 rewriter.modifyOpInPlace(op, [&]() {
590 LogicalResult handleDefaultRewrite(
591 Attribute, MemberReadOp op, OpAdaptor, ConversionPatternRewriter &, Attribute a
593 return op->emitOpError().append(
594 "table offset requires an integer template value, but found ", a
598 LogicalResult matchAndRewrite(
599 MemberReadOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
601 LLVM_DEBUG(llvm::dbgs() <<
"[ClonedMemberReadOpPattern] MemberReadOp: " << op <<
'\n';);
602 if (tableOffsetIsntSymbol(op)) {
606 return super::matchAndRewrite(op, adaptor, rewriter);
615 ConversionTracker &tracker_;
617 SymbolTableCollection symTables;
618 bool reportMissing =
true;
620 class MappedTypeConverter :
public TypeConverter {
623 const DenseMap<Attribute, Attribute> ¶mNameToValue;
625 inline Attribute convertIfPossible(Attribute a)
const {
626 auto res = this->paramNameToValue.find(a);
627 return (res != this->paramNameToValue.end()) ? res->second : a;
634 const DenseMap<Attribute, Attribute> ¶mNameToInstantiatedValue
636 : TypeConverter(), origTy(originalType), newTy(newType),
637 paramNameToValue(paramNameToInstantiatedValue) {
639 addConversion([](Type inputTy) {
return inputTy; });
642 LLVM_DEBUG(llvm::dbgs() <<
"[MappedTypeConverter] convert " << inputTy <<
'\n');
645 if (inputTy == this->origTy) {
649 if (ArrayAttr inputTyParams = inputTy.getParams()) {
650 SmallVector<Attribute> updated;
651 for (Attribute a : inputTyParams) {
652 if (TypeAttr ta = dyn_cast<TypeAttr>(a)) {
653 updated.push_back(TypeAttr::get(this->convertType(ta.getValue())));
655 updated.push_back(convertIfPossible(a));
664 addConversion([
this](
ArrayType inputTy) {
666 ArrayRef<Attribute> dimSizes = inputTy.getDimensionSizes();
667 if (!dimSizes.empty()) {
668 SmallVector<Attribute> updated;
669 for (Attribute a : dimSizes) {
670 updated.push_back(convertIfPossible(a));
672 return ArrayType::get(this->convertType(inputTy.getElementType()), updated);
678 addConversion([
this](
TypeVarType inputTy) -> Type {
680 if (TypeAttr tyAttr = llvm::dyn_cast<TypeAttr>(convertIfPossible(inputTy.getNameRef()))) {
681 Type convertedType = tyAttr.getValue();
686 return convertedType;
694 FailureOr<StructType> genClone(
StructType typeAtCaller, ArrayRef<Attribute> typeAtCallerParams) {
695 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] attempting clone of " << typeAtCaller <<
'\n');
697 FailureOr<SymbolLookupResult<StructDefOp>> r =
698 typeAtCaller.
getDefinition(symTables, rootMod, reportMissing);
700 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: cannot find StructDefOp \n");
703 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] found definition\n";);
707 MLIRContext *ctx = origStruct.getContext();
709 assert(parentTemplate &&
"parameterized struct must be nested in a TemplateOp");
711 assert(parentModule &&
"TemplateOp must be nested in a ModuleOp");
714 DenseMap<Attribute, Attribute> paramNameToConcrete;
716 ArrayAttr reducedCallerParams =
nullptr;
717 SmallVector<Attribute> nonConcreteParams;
719 ArrayAttr paramNames = typeAtDef.
getParams();
723 assert(paramNames.size() == typeAtCallerParams.size());
725 for (
size_t i = 0, e = paramNames.size(); i < e; ++i) {
726 Attribute next = typeAtCallerParams[i];
727 if (isConcreteAttr<false>(next)) {
728 paramNameToConcrete[paramNames[i]] = next;
730 nonConcreteParams.push_back(next);
734 assert(nonConcreteParams.size() + paramNameToConcrete.size() == paramNames.size());
736 if (paramNameToConcrete.empty()) {
737 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: no concrete params \n");
740 if (!nonConcreteParams.empty()) {
741 reducedCallerParams = ArrayAttr::get(ctx, nonConcreteParams);
745 FailureOr<InstantiationLayout> layoutResult =
747 if (failed(layoutResult)) {
754 SmallVector<FlatSymbolRefAttr> typeAtCallerSymPieces =
getPieces(typeAtCaller.
getNameRef());
755 typeAtCallerSymPieces.pop_back();
759 evaluateTemplateExprs(parentTemplate, paramNameToConcrete);
763 convertCalleesInPlace(newStruct, paramNameToConcrete);
771 symTables.getSymbolTable(parentModule).insert(newStruct, Block::iterator(parentTemplate));
773 typeAtCallerSymPieces.pop_back();
776 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
779 assert(newTemplate->getNumRegions() > 0 &&
"region exists");
784 FlatSymbolRefAttr nameSym = llvm::dyn_cast<FlatSymbolRefAttr>(name);
785 assert(nameSym &&
"expected FlatSymbolRefAttr");
787 Operation *symOp = symTables.getSymbolTable(parentTemplate).lookup(nameSym.getAttr());
788 assert(symOp &&
"symbol must exist");
789 newTemplate.insert(newTemplate.begin(), symOp->clone());
794 symTables.getSymbolTable(newTemplate).insert(newStruct);
795 symTables.getSymbolTable(parentModule).insert(newTemplate, Block::iterator(parentTemplate));
799 typeAtCallerSymPieces.back() = FlatSymbolRefAttr::get(newTemplate.
getSymNameAttr());
805 typeAtCallerSymPieces.push_back(
806 FlatSymbolRefAttr::get(newLocalType.
getNameRef().getLeafReference())
811 llvm::dbgs() <<
"[StructCloner] original def type: " << typeAtDef <<
'\n';
812 llvm::dbgs() <<
"[StructCloner] cloned def type: " << newStruct.
getType() <<
'\n';
813 llvm::dbgs() <<
"[StructCloner] original remote type: " << typeAtCaller <<
'\n';
814 llvm::dbgs() <<
"[StructCloner] cloned local type: " << newLocalType <<
'\n';
815 llvm::dbgs() <<
"[StructCloner] cloned remote type: " << newRemoteType <<
'\n';
821 MappedTypeConverter tyConv(typeAtDef, newStruct.
getType(), paramNameToConcrete);
822 ConversionTarget target =
826 return !paramNameToConcrete.contains(op.getConstNameAttr());
830 patterns.add<ClonedBodyConstReadOpPattern>(
831 tyConv, ctx, paramNameToConcrete, tracker_.delayedDiagnosticSet(newLocalType)
833 patterns.add<ClonedMemberReadOpPattern>(tyConv, ctx, paramNameToConcrete);
834 if (failed(applyFullConversion(newStruct, target, std::move(patterns)))) {
835 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] instantiating body of struct failed \n");
838 return newRemoteType;
842 StructCloner(ConversionTracker &tracker, ModuleOp root)
843 : tracker_(tracker), rootMod(root), symTables() {}
845 FailureOr<StructType> createInstantiatedClone(
StructType orig) {
846 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] orig: " << orig <<
'\n');
847 if (ArrayAttr params = orig.
getParams()) {
848 return genClone(orig, params.getValue());
850 LLVM_DEBUG(llvm::dbgs() <<
"[StructCloner] skip: nullptr for params \n");
854 void enableReportMissing() { reportMissing =
true; }
856 void disableReportMissing() { reportMissing =
false; }
859class DisableReportMissing;
861class ParameterizedStructUseTypeConverter :
public TypeConverter {
862 ConversionTracker &tracker_;
865 friend DisableReportMissing;
868 ParameterizedStructUseTypeConverter(ConversionTracker &tracker, ModuleOp root)
869 : TypeConverter(), tracker_(tracker), cloner(tracker, root) {
871 addConversion([](Type inputTy) {
return inputTy; });
875 llvm::dbgs() <<
"[ParameterizedStructUseTypeConverter] attempting conversion of "
879 if (
auto opt = tracker_.getInstantiation(inputTy)) {
885 FailureOr<StructType> cloneRes = cloner.createInstantiatedClone(inputTy);
886 if (failed(cloneRes)) {
891 llvm::dbgs() <<
"[ParameterizedStructUseTypeConverter] instantiating " << inputTy
892 <<
" as " << newTy <<
'\n'
894 tracker_.recordInstantiation(inputTy, newTy);
898 addConversion([
this](
ArrayType inputTy) {
899 return inputTy.cloneWith(convertType(inputTy.getElementType()));
904class CallStructFuncPattern :
public OpConversionPattern<CallOp> {
905 ConversionTracker &tracker_;
908 CallStructFuncPattern(TypeConverter &converter, MLIRContext *ctx, ConversionTracker &tracker)
910 : OpConversionPattern<CallOp>(converter, ctx, 1), tracker_(tracker) {}
912 LogicalResult matchAndRewrite(
913 CallOp op, OpAdaptor adapter, ConversionPatternRewriter &rewriter
915 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] CallOp: " << op <<
'\n');
918 SmallVector<Type> newResultTypes;
919 if (failed(getTypeConverter()->convertTypes(op.getResultTypes(), newResultTypes))) {
920 return op->emitError(
"Could not convert Op result types.");
923 llvm::dbgs() <<
"[CallStructFuncPattern] newResultTypes: "
933 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] newStTy: " << newStTy <<
'\n');
934 calleeAttr =
appendLeaf(newStTy.getNameRef(), calleeAttr.getLeafReference());
935 tracker_.reportDelayedDiagnostics(newStTy, op);
939 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] newStTy: " << newStTy <<
'\n');
940 calleeAttr =
appendLeaf(newStTy.getNameRef(), calleeAttr.getLeafReference());
944 LLVM_DEBUG(llvm::dbgs() <<
"[CallStructFuncPattern] replaced " << op);
946 rewriter, op, newResultTypes, calleeAttr, adapter.
getMapOperands(),
950 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
956class MemberDefOpPattern :
public OpConversionPattern<MemberDefOp> {
958 MemberDefOpPattern(TypeConverter &converter, MLIRContext *ctx, ConversionTracker &)
960 : OpConversionPattern<MemberDefOp>(converter, ctx, 1) {}
962 LogicalResult matchAndRewrite(
963 MemberDefOp op, OpAdaptor , ConversionPatternRewriter &rewriter
965 LLVM_DEBUG(llvm::dbgs() <<
"[MemberDefOpPattern] MemberDefOp: " << op <<
'\n');
967 Type oldMemberType = op.
getType();
968 Type newMemberType = getTypeConverter()->convertType(oldMemberType);
969 if (oldMemberType == newMemberType) {
972 rewriter.modifyOpInPlace(op, [&op, &newMemberType]() { op.
setType(newMemberType); });
980 ParameterizedStructUseTypeConverter &tyConv;
983 explicit DisableReportMissing(ParameterizedStructUseTypeConverter &tc) : tyConv(tc) {}
985 void checkStarted()
override { tyConv.cloner.disableReportMissing(); }
987 void checkEnded(
bool)
override { tyConv.cloner.enableReportMissing(); }
990LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
991 MLIRContext *ctx = modOp.getContext();
992 ParameterizedStructUseTypeConverter tyConv(tracker, modOp);
993 DisableReportMissing drm(tyConv);
996 patterns.add<CallStructFuncPattern, MemberDefOpPattern>(tyConv, ctx, tracker);
997 return applyPartialConversion(modOp, target, std::move(patterns));
1002LogicalResult instantiateMainStruct(ModuleOp modOp, ConversionTracker &tracker) {
1004 if (failed(mainTypeOpt)) {
1013 StructCloner cloner(tracker, modOp);
1014 FailureOr<StructType> cloneRes = cloner.createInstantiatedClone(mainType);
1015 if (failed(cloneRes)) {
1019 StructType instantiatedMainType = cloneRes.value();
1020 tracker.recordInstantiation(mainType, instantiatedMainType);
1021 modOp->setAttr(
MAIN_ATTR_NAME, TypeAttr::get(instantiatedMainType));
1031class FuncInstTypeConverter :
public TypeConverter {
1032 DenseMap<Attribute, Attribute> paramNameToValue;
1034 Attribute convertIfPossible(Attribute a)
const {
1035 auto res = paramNameToValue.find(a);
1036 return (res != paramNameToValue.end()) ? res->second : a;
1040 explicit FuncInstTypeConverter(DenseMap<Attribute, Attribute> paramNameToConcrete)
1041 : TypeConverter(), paramNameToValue(std::move(paramNameToConcrete)) {
1042 addConversion([](Type t) {
return t; });
1044 addConversion([
this](
TypeVarType inputTy) -> Type {
1045 if (TypeAttr tyAttr = llvm::dyn_cast<TypeAttr>(convertIfPossible(inputTy.
getNameRef()))) {
1046 Type convertedType = tyAttr.getValue();
1048 return convertedType;
1054 addConversion([
this](
ArrayType inputTy) {
1055 SmallVector<Attribute> updated;
1056 bool changed =
false;
1057 for (Attribute a : inputTy.getDimensionSizes()) {
1058 Attribute converted = convertIfPossible(a);
1059 updated.push_back(converted);
1060 if (converted != a) {
1064 Type newElemTy = this->convertType(inputTy.getElementType());
1065 if (!changed && newElemTy == inputTy.getElementType()) {
1069 inputTy.cloneWith(inputTy.getElementType(), updated), newElemTy
1074 if (ArrayAttr params = inputTy.getParams()) {
1075 SmallVector<Attribute> updated;
1076 bool changed =
false;
1077 for (Attribute a : params) {
1078 if (TypeAttr ta = dyn_cast<TypeAttr>(a)) {
1079 Type newTy = this->convertType(ta.getValue());
1080 if (newTy != ta.getValue()) {
1081 updated.push_back(TypeAttr::get(newTy));
1086 Attribute converted = convertIfPossible(a);
1087 if (converted != a) {
1088 updated.push_back(converted);
1093 updated.push_back(a);
1103 Attribute convertAttr(Attribute attr)
const {
1104 if (TypeAttr tyAttr = llvm::dyn_cast<TypeAttr>(attr)) {
1105 Type convertedTy = convertType(tyAttr.getValue());
1106 if (convertedTy != tyAttr.getValue()) {
1107 return TypeAttr::get(convertedTy);
1110 return convertIfPossible(attr);
1113 bool containsParam(Attribute nameAttr)
const {
return paramNameToValue.contains(nameAttr); }
1114 const DenseMap<Attribute, Attribute> &getParamMap()
const {
return paramNameToValue; }
1118inline static std::optional<Attribute>
1119inferUnifiedParam(
const UnificationMap &unifyResult, SymbolRefAttr paramName) {
1120 auto it = unifyResult.find({paramName,
Side::RHS});
1121 return (it == unifyResult.end()) ? std::nullopt : std::make_optional(it->second);
1126inline static LogicalResult failIncompatibleInferredParam(
1130 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
1131 <<
"': incompatible with specified param type. MUST FAIL!\n"
1133 return rewriter.notifyMatchFailure(op, [¶mName, ¶mOp](Diagnostic &diag) {
1134 diag.append(
"inferred value for parameter '")
1136 .append(
"' is incompatible with specified param type")
1137 .attachNote(paramOp.getLoc())
1138 .append(
"template parameter declared here");
1144class WildcardTypeBodyInferer final {
1145 SymbolTableCollection &symTables_;
1146 const DenseMap<Attribute, Attribute> ¶mNameToConcrete_;
1147 SmallVector<std::pair<Operation *, FlatSymbolRefAttr>> activeInferences_;
1150 WildcardTypeBodyInferer(
1151 SymbolTableCollection &symTables,
const DenseMap<Attribute, Attribute> ¶mNameToConcrete
1153 : symTables_(symTables), paramNameToConcrete_(paramNameToConcrete) {}
1155 std::optional<Attribute> infer(
FuncDefOp func, FlatSymbolRefAttr paramName) {
1156 if (llvm::any_of(activeInferences_, [&](
const auto &e) {
1157 return e.first == func.getOperation() && e.second == paramName;
1159 return std::nullopt;
1161 activeInferences_.emplace_back(func.getOperation(), paramName);
1163 FuncInstTypeConverter tyConv((paramNameToConcrete_));
1164 std::optional<Attribute> inferred;
1165 bool ambiguous =
false;
1169 auto noteCandidate = [&inferred, &ambiguous](Attribute candidate) {
1170 if (!candidate || !isConcreteAttr(candidate)) {
1171 return WalkResult::advance();
1173 if (!inferred.has_value()) {
1174 inferred = candidate;
1175 return WalkResult::advance();
1177 if (*inferred != candidate) {
1179 return WalkResult::interrupt();
1181 return WalkResult::advance();
1184 WalkResult walkResult = func.walk([&](Operation *bodyOp) {
1185 if (
auto castOp = llvm::dyn_cast<UnifiableCastOp>(bodyOp)) {
1186 Type inputTy = tyConv.convertType(castOp.getInput().getType());
1187 Type resultTy = tyConv.convertType(castOp.getResult().getType());
1188 if (
auto inputTvar = llvm::dyn_cast<TypeVarType>(inputTy);
1189 inputTvar && inputTvar.getNameRef() == paramName &&
isConcreteType(resultTy)) {
1190 return noteCandidate(TypeAttr::get(resultTy));
1192 if (
auto resultTvar = llvm::dyn_cast<TypeVarType>(resultTy);
1193 resultTvar && resultTvar.getNameRef() == paramName &&
isConcreteType(inputTy)) {
1194 return noteCandidate(TypeAttr::get(inputTy));
1196 return WalkResult::advance();
1199 auto nestedCall = llvm::dyn_cast<CallOp>(bodyOp);
1201 return WalkResult::advance();
1204 FailureOr<SymbolLookupResult<FuncDefOp>> nestedTgtOpt =
1205 nestedCall.getCalleeTarget(symTables_);
1206 if (failed(nestedTgtOpt)) {
1207 return WalkResult::advance();
1209 FuncDefOp nestedTgt = nestedTgtOpt->get();
1210 auto nestedTemplate = llvm::dyn_cast<TemplateOp>(nestedTgt->getParentOp());
1211 if (!nestedTemplate) {
1212 return WalkResult::advance();
1215 TypeRange nestedResultTypes = nestedTgt.
getFunctionType().getResults();
1216 for (
auto [result, nestedResultTy] :
1217 llvm::zip_equal(nestedCall.getResults(), nestedResultTypes)) {
1218 Type convertedResultTy = tyConv.convertType(result.getType());
1219 auto resultTvar = llvm::dyn_cast<TypeVarType>(convertedResultTy);
1220 auto nestedTvar = llvm::dyn_cast<TypeVarType>(nestedResultTy);
1221 if (!resultTvar || !nestedTvar || resultTvar.getNameRef() != paramName) {
1224 if (std::optional<Attribute> candidate = inferFromExplicitNestedCallParams(
1225 nestedCall, nestedTemplate, nestedTvar.getNameRef(), tyConv
1227 WalkResult candidateResult = noteCandidate(*candidate);
1228 if (candidateResult.wasInterrupted()) {
1229 return candidateResult;
1233 if (std::optional<Attribute> candidate = infer(nestedTgt, nestedTvar.getNameRef())) {
1234 WalkResult candidateResult = noteCandidate(*candidate);
1235 if (candidateResult.wasInterrupted()) {
1236 return candidateResult;
1240 return WalkResult::advance();
1243 activeInferences_.pop_back();
1244 if (ambiguous || (walkResult.wasInterrupted() && !inferred.has_value())) {
1245 return std::nullopt;
1251 std::optional<Attribute> inferFromExplicitNestedCallParams(
1252 CallOp nestedCall,
TemplateOp nestedTemplate, FlatSymbolRefAttr nestedParamName,
1253 const FuncInstTypeConverter &tyConv
1257 return std::nullopt;
1260 for (
auto [paramOp, attr] :
1262 auto paramName = FlatSymbolRefAttr::get(paramOp.
getSymNameAttr());
1263 if (paramName != nestedParamName) {
1266 Attribute convertedAttr = tyConv.convertAttr(attr);
1267 return isConcreteAttr(convertedAttr) ? std::make_optional(convertedAttr) : std::nullopt;
1269 return std::nullopt;
1275class ClonedBodyArrayReadOpPattern final :
public OpConversionPattern<ReadArrayOp> {
1277 using OpConversionPattern<
ReadArrayOp>::OpConversionPattern;
1279 LogicalResult matchAndRewrite(
1280 ReadArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
1282 Type newResultTy = getTypeConverter()->convertType(op.
getResult().getType());
1283 if (!llvm::isa<ArrayType>(newResultTy)) {
1287 rewriter, op, newResultTy, adaptor.
getArrRef(), adaptor.getIndices()
1295class ClonedBodyArrayWriteOpPattern final :
public OpConversionPattern<WriteArrayOp> {
1297 using OpConversionPattern<
WriteArrayOp>::OpConversionPattern;
1299 LogicalResult matchAndRewrite(
1300 WriteArrayOp op, OpAdaptor adaptor, ConversionPatternRewriter &rewriter
1302 if (!llvm::isa<ArrayType>(adaptor.getRvalue().getType())) {
1306 rewriter, op, adaptor.
getArrRef(), adaptor.getIndices(), adaptor.getRvalue()
1315static LogicalResult applyBodyConversions(
1316 CallOp op,
FuncDefOp newFunc,
const DenseMap<Attribute, Attribute> ¶mNameToConcrete
1318 MLIRContext *ctx = op.getContext();
1319 FuncInstTypeConverter tyConv(paramNameToConcrete);
1323 return !tyConv.containsParam(p.getConstNameAttr());
1325 SmallVector<Diagnostic> delayedDiagnostics;
1327 bodyPatterns.add<ClonedBodyConstReadOpPattern>(
1328 tyConv, ctx, tyConv.getParamMap(), delayedDiagnostics
1330 bodyPatterns.add<ClonedBodyArrayReadOpPattern, ClonedBodyArrayWriteOpPattern>(tyConv, ctx);
1331 bodyPatterns.add<ClonedMemberReadOpPattern>(tyConv, ctx, paramNameToConcrete);
1332 if (failed(applyFullConversion(newFunc, target, std::move(bodyPatterns)))) {
1335 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] instantiated clone: " << newFunc <<
'\n');
1336 ::reportDelayedDiagnostics(op, std::move(delayedDiagnostics));
1338 SymbolTableCollection tables;
1339 WalkResult res = newFunc.walk([&tables](
CallOp nestedCall) {
1342 return failure(res.wasInterrupted());
1345class InstantiateFuncAtCallOp final :
public OpRewritePattern<CallOp> {
1346 ConversionTracker &tracker_;
1349 InstantiateFuncAtCallOp(MLIRContext *ctx, ConversionTracker &tracker)
1350 : OpRewritePattern<CallOp>(ctx), tracker_(tracker) {}
1352 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
1353 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] op: " << op <<
'\n');
1355 if (calleeReferencesTemplateParam(op)) {
1360 SymbolTableCollection symTables;
1361 FailureOr<SymbolLookupResult<FuncDefOp>> callTgtOpt = op.
getCalleeTarget(symTables);
1362 if (failed(callTgtOpt)) {
1363 return rewriter.notifyMatchFailure(op, [](Diagnostic &diag) {
1364 diag <<
"could not find target function for call";
1370 TemplateOp parentTemplate = llvm::dyn_cast<TemplateOp>(callTgt->getParentOp());
1371 if (!parentTemplate) {
1375 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] target function in template "
1386 FailureOr<UnificationMap> unifyResult = unifyTypeSignature(op, callTgt, rewriter);
1387 if (failed(unifyResult)) {
1391 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unifications of types: "
1396 DenseMap<Attribute, Attribute> paramNameToConcrete;
1397 if (failed(collectConcreteTemplateParams(
1398 op, rewriter, symTables, callTgt, parentTemplate, unifyResult.value(),
1404 if (paramNameToConcrete.empty()) {
1405 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateFuncAtCallOp] skip: no concrete params\n");
1409 evaluateTemplateExprs(parentTemplate, paramNameToConcrete);
1411 FailureOr<InstantiationLayout> layoutResult =
1413 if (failed(layoutResult)) {
1418 assert(parentModule &&
"TemplateOp must be nested in a ModuleOp");
1421 FailureOr<SymbolRefAttr> newCalleeAttr =
1424 op, rewriter, symTables, callTgt, parentTemplate, parentModule,
1427 : instantiatePartially(
1428 op, rewriter, symTables, callTgt, parentTemplate, parentModule, layout,
1429 paramNameToConcrete, tracker_
1431 if (failed(newCalleeAttr)) {
1435 tracker_.recordInstantiation(originalCalleeAttr);
1438 rewriter.modifyOpInPlace(op, [&op, &newCalleeAttr, &layout]() {
1440 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] updating callee from " << op.
getCalleeAttr()
1441 <<
" to " << *newCalleeAttr <<
'\n';
1446 tracker_.updateModifiedFlag(
true);
1453 static FailureOr<UnificationMap>
1454 unifyTypeSignature(
CallOp op,
FuncDefOp callTgt, PatternRewriter &rewriter) {
1456 if (succeeded(unifyResult)) {
1459 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1460 diag.append(
"target function type does not unify with call type ")
1462 .attachNote(callTgt.getLoc())
1463 .append(
"target function declared here");
1469 static LogicalResult collectConcreteTemplateParams(
1470 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
1472 DenseMap<Attribute, Attribute> ¶mNameToConcrete
1477 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] TemplateParamsAttr: " << callParams <<
'\n'
1480 auto recordConcreteParam = [&](FlatSymbolRefAttr paramName,
TemplateParamOp paramOp,
1481 Attribute concreteValue) {
1483 return failIncompatibleInferredParam(op, rewriter, paramName, paramOp);
1485 paramNameToConcrete[paramName] = concreteValue;
1491 for (
auto paramOp : realParams) {
1492 auto paramName = FlatSymbolRefAttr::get(paramOp.getSymNameAttr());
1493 auto inferredValOpt = inferUnifiedParam(unifyResult, paramName);
1494 if (!inferredValOpt.has_value()) {
1496 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
1501 Attribute inferredVal = *inferredValOpt;
1503 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] inferredVal: " << inferredVal <<
'\n'
1505 if (!isConcreteAttr(inferredVal)) {
1507 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
1508 <<
"': not concrete, " << inferredVal <<
'\n'
1512 if (failed(recordConcreteParam(paramName, paramOp, inferredVal))) {
1521 assert((callParams.size() == llvm::range_size(realParams)) &&
"per CallOpVerifier");
1523 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1524 diag.append(
"incompatible with specified param type(s)");
1528 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1529 diag.append(
"incompatible with inferred param value(s)");
1535 SmallVector<std::pair<TemplateParamOp, FlatSymbolRefAttr>> wildcardParams;
1536 for (
auto [paramOp, attr] : llvm::zip_equal(realParams, callParams.getValue())) {
1537 auto paramName = FlatSymbolRefAttr::get(paramOp.getSymNameAttr());
1540 paramNameToConcrete[paramName] = attr;
1546 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] unification for param '" << paramName
1547 <<
"': not concrete, " << attr <<
'\n'
1551 wildcardParams.emplace_back(paramOp, paramName);
1554 WildcardTypeBodyInferer bodyInferer(symTables, paramNameToConcrete);
1555 for (
auto [paramOp, paramName] : wildcardParams) {
1556 auto inferredValOpt = inferUnifiedParam(unifyResult, paramName);
1557 if (inferredValOpt.has_value() && isConcreteAttr(*inferredValOpt)) {
1559 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] inferredVal: " << *inferredValOpt <<
'\n'
1561 if (failed(recordConcreteParam(paramName, paramOp, *inferredValOpt))) {
1567 inferredValOpt = bodyInferer.infer(callTgt, paramName);
1568 if (inferredValOpt.has_value() && isConcreteAttr(*inferredValOpt)) {
1570 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body-inferred value for param '"
1571 << paramName <<
"': " << *inferredValOpt <<
'\n'
1573 if (failed(recordConcreteParam(paramName, paramOp, *inferredValOpt))) {
1583 static FailureOr<SymbolRefAttr> instantiateFully(
1584 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
1585 TemplateOp parentTemplate, ModuleOp parentModule, StringRef templateNameWithAttrs,
1586 const DenseMap<Attribute, Attribute> ¶mNameToConcrete
1588 MLIRContext *ctx = op.getContext();
1589 std::string newFuncName =
1590 (mlir::Twine(templateNameWithAttrs) +
"_" + callTgt.
getSymName()).str();
1591 StringRef actualNewFuncName = newFuncName;
1592 if (!symTables.getSymbolTable(parentModule).lookup(newFuncName)) {
1595 convertCalleesInPlace(newFunc, paramNameToConcrete);
1597 symTables.getSymbolTable(parentModule).insert(newFunc, Block::iterator(parentTemplate));
1600 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] created full instantiation function: "
1601 << actualNewFuncName <<
'\n'
1603 if (failed(applyBodyConversions(op, newFunc, paramNameToConcrete))) {
1605 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body conversion failed for "
1606 << actualNewFuncName <<
'\n'
1609 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1610 diag.append(
"failure while creating instantiated function '", actualNewFuncName,
'\'');
1615 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] reusing full instantiation function: "
1616 << actualNewFuncName <<
'\n'
1624 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1625 symPieces.pop_back();
1626 symPieces.pop_back();
1627 symPieces.push_back(FlatSymbolRefAttr::get(StringAttr::get(ctx, actualNewFuncName)));
1635 static FailureOr<SymbolRefAttr> instantiatePartially(
1636 CallOp op, PatternRewriter &rewriter, SymbolTableCollection &symTables,
FuncDefOp callTgt,
1638 const DenseMap<Attribute, Attribute> ¶mNameToConcrete, ConversionTracker &tracker
1640 if (
auto cached = tracker.lookupPartialFuncInstantiation(callTgt, layout.
concreteParamKey)) {
1642 SmallVector<FlatSymbolRefAttr> cachedSuffix =
getPieces(*cached);
1643 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1644 assert(cachedSuffix.size() == 2 &&
"cached callee suffix must contain template and function");
1645 symPieces.pop_back();
1646 symPieces.pop_back();
1647 symPieces.push_back(cachedSuffix[0]);
1648 symPieces.push_back(cachedSuffix[1]);
1651 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] reusing partial instantiation: "
1652 << cachedCallee <<
'\n'
1654 return cachedCallee;
1656 TemplateOp newTemplate = parentTemplate.cloneWithoutRegions();
1659 assert(newTemplate->getNumRegions() > 0 &&
"region exists");
1662 Block &newTemplateBody = newTemplate.
getBodyRegion().front();
1664 FlatSymbolRefAttr nameSym = llvm::cast<FlatSymbolRefAttr>(name);
1665 Operation *paramOp = symTables.getSymbolTable(parentTemplate).lookup(nameSym.getAttr());
1666 assert(paramOp &&
"symbol must exist");
1667 newTemplateBody.push_back(paramOp->clone());
1672 convertCalleesInPlace(newFunc, paramNameToConcrete);
1676 symTables.getSymbolTable(newTemplate).insert(newFunc);
1677 symTables.getSymbolTable(parentModule).insert(newTemplate, Block::iterator(parentTemplate));
1678 if (failed(applyBodyConversions(op, newFunc, paramNameToConcrete))) {
1679 std::string newFuncName = newFunc.
getSymName().str();
1681 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] body conversion failed for " << newFuncName
1684 newTemplate->erase();
1685 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1686 diag.append(
"failure while creating instantiated function '", newFuncName,
'\'');
1692 assert(symPieces.size() >= 2 &&
"callee must include at least template and function names");
1693 symPieces.pop_back();
1694 symPieces.pop_back();
1695 symPieces.push_back(FlatSymbolRefAttr::get(newTemplate.
getSymNameAttr()));
1696 symPieces.push_back(FlatSymbolRefAttr::get(newFunc.
getSymNameAttr()));
1700 llvm::dbgs() <<
"[InstantiateFuncAtCallOp] created partial instantiation: " << newCallee
1704 tracker.recordPartialFuncInstantiation(callTgt, layout.
concreteParamKey, newTemplate, newFunc);
1709LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1710 MLIRContext *ctx = modOp.getContext();
1711 RewritePatternSet patterns(ctx);
1712 patterns.add<InstantiateFuncAtCallOp>(ctx, tracker);
1713 MatchFailureListener failureListener;
1714 walkAndApplyPatterns(modOp, std::move(patterns), &failureListener);
1715 return failure(failureListener.hadFailure);
1723template <HasInterface<LoopLikeOpInterface> OpClass>
1724class LoopUnrollPattern :
public OpRewritePattern<OpClass> {
1726 using OpRewritePattern<OpClass>::OpRewritePattern;
1728 LogicalResult matchAndRewrite(OpClass loopOp, PatternRewriter &rewriter)
const override {
1729 if (
auto maybeConstant = getConstantTripCount(loopOp)) {
1730 uint64_t tripCount = *maybeConstant;
1731 if (tripCount == 0) {
1732 rewriter.eraseOp(loopOp);
1734 }
else if (tripCount == 1) {
1735 return loopOp.promoteIfSingleIteration(rewriter);
1737 return loopUnrollByFactor(loopOp, tripCount);
1745 static std::optional<int64_t> getConstantTripCount(LoopLikeOpInterface loopOp) {
1746 std::optional<OpFoldResult> lbVal = loopOp.getSingleLowerBound();
1747 std::optional<OpFoldResult> ubVal = loopOp.getSingleUpperBound();
1748 std::optional<OpFoldResult> stepVal = loopOp.getSingleStep();
1749 if (!lbVal.has_value() || !ubVal.has_value() || !stepVal.has_value()) {
1750 return std::nullopt;
1752 return constantTripCount(lbVal.value(), ubVal.value(), stepVal.value());
1756LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
1757 MLIRContext *ctx = modOp.getContext();
1758 RewritePatternSet patterns(ctx);
1759 patterns.add<LoopUnrollPattern<scf::ForOp>>(ctx);
1760 patterns.add<LoopUnrollPattern<affine::AffineForOp>>(ctx);
1762 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
1770std::optional<SmallVector<int64_t>> getConstantIntValues(ArrayRef<OpFoldResult> ofrs) {
1771 SmallVector<int64_t> res;
1772 for (OpFoldResult ofr : ofrs) {
1773 std::optional<int64_t> cv = getConstantIntValue(ofr);
1774 if (!cv.has_value()) {
1775 return std::nullopt;
1777 res.push_back(cv.value());
1782struct AffineMapFolder {
1784 OperandRangeRange mapOpGroups;
1785 DenseI32ArrayAttr dimsPerGroup;
1786 ArrayRef<Attribute> paramsOfStructTy;
1790 SmallVector<SmallVector<Value>> mapOpGroups;
1791 SmallVector<int32_t> dimsPerGroup;
1792 SmallVector<Attribute> paramsOfStructTy;
1795 static inline SmallVector<ValueRange> getConvertedMapOpGroups(Output out) {
1796 return llvm::map_to_vector(out.mapOpGroups, [](
const SmallVector<Value> &grp) {
1797 return ValueRange(grp);
1801 static LogicalResult
1802 fold(PatternRewriter &rewriter,
const Input &in, Output &out, Operation *op,
const char *aspect) {
1803 if (in.mapOpGroups.empty()) {
1808 assert(in.mapOpGroups.size() <= in.paramsOfStructTy.size());
1809 assert(std::cmp_equal(in.mapOpGroups.size(), in.dimsPerGroup.size()));
1812 for (Attribute sizeAttr : in.paramsOfStructTy) {
1813 if (AffineMapAttr m = dyn_cast<AffineMapAttr>(sizeAttr)) {
1814 ValueRange currMapOps = in.mapOpGroups[idx++];
1819 SmallVector<OpFoldResult> currMapOpsCast = getAsOpFoldResult(currMapOps);
1821 llvm::dbgs() <<
"[AffineMapFolder] currMapOps as fold results: "
1824 if (
auto constOps = Step4_InstantiateAffineMaps::getConstantIntValues(currMapOpsCast)) {
1825 SmallVector<Attribute> result;
1826 bool hasPoison =
false;
1827 auto constAttrs = llvm::map_to_vector(*constOps, [&rewriter](int64_t v) -> Attribute {
1828 return rewriter.getIndexAttr(v);
1830 LogicalResult foldResult = m.getAffineMap().constantFold(constAttrs, result, &hasPoison);
1835 "Cannot fold affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
1836 " due to divide by 0 or modulus with negative divisor"
1841 if (failed(foldResult)) {
1845 "Folding affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
" failed"
1850 if (result.size() != 1) {
1854 "Folding affine_map for ", aspect,
' ', out.paramsOfStructTy.size(),
1855 " produced ", result.size(),
" results but expected 1"
1860 assert(!llvm::isa<AffineMapAttr>(result[0]) &&
"not converted");
1861 out.paramsOfStructTy.push_back(result[0]);
1865 out.mapOpGroups.emplace_back(currMapOps);
1866 out.dimsPerGroup.push_back(in.dimsPerGroup[idx - 1]);
1869 out.paramsOfStructTy.push_back(sizeAttr);
1871 assert(idx == in.mapOpGroups.size() &&
"all affine_map not processed");
1873 in.paramsOfStructTy.size() == out.paramsOfStructTy.size() &&
1874 "produced wrong number of dimensions"
1882class InstantiateAtCreateArrayOp final :
public OpRewritePattern<CreateArrayOp> {
1884 ConversionTracker &tracker_;
1887 InstantiateAtCreateArrayOp(MLIRContext *ctx, ConversionTracker &tracker)
1888 : OpRewritePattern(ctx), tracker_(tracker) {}
1890 LogicalResult matchAndRewrite(
CreateArrayOp op, PatternRewriter &rewriter)
const override {
1893 AffineMapFolder::Output out;
1894 AffineMapFolder::Input in = {
1899 if (failed(AffineMapFolder::fold(rewriter, in, out, op,
"array dimension"))) {
1904 if (newResultType == oldResultType) {
1908 assert(tracker_.isLegalConversion(oldResultType, newResultType,
"InstantiateAtCreateArrayOp"));
1910 llvm::dbgs() <<
"[InstantiateAtCreateArrayOp] instantiating " << oldResultType <<
" as "
1911 << newResultType <<
" in \"" << op <<
"\"\n"
1914 rewriter, op, newResultType, AffineMapFolder::getConvertedMapOpGroups(out), out.dimsPerGroup
1921class InstantiateAtCallOpCompute final :
public OpRewritePattern<CallOp> {
1922 ConversionTracker &tracker_;
1925 InstantiateAtCallOpCompute(MLIRContext *ctx, ConversionTracker &tracker)
1926 : OpRewritePattern(ctx), tracker_(tracker) {}
1928 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
1933 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] target: " << op.
getCallee() <<
'\n');
1935 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] oldRetTy: " << oldRetTy <<
'\n');
1936 ArrayAttr params = oldRetTy.
getParams();
1942 AffineMapFolder::Output out;
1943 AffineMapFolder::Input in = {
1948 if (!in.mapOpGroups.empty()) {
1950 if (failed(AffineMapFolder::fold(rewriter, in, out, op,
"struct parameter"))) {
1954 llvm::dbgs() <<
"[InstantiateAtCallOpCompute] folded affine_map in result type params\n";
1960 if (callArgTypes.empty()) {
1964 if (calleeReferencesTemplateParam(op)) {
1967 SymbolTableCollection tables;
1969 if (failed(lookupRes)) {
1972 if (failed(instantiateViaTargetType(in, out, callArgTypes, lookupRes->get()))) {
1976 llvm::dbgs() <<
"[InstantiateAtCallOpCompute] propagated instantiations via symrefs in "
1977 "result type params: "
1983 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] newRetTy: " << newRetTy <<
'\n');
1984 if (newRetTy == oldRetTy) {
1991 if (!tracker_.isLegalConversion(oldRetTy, newRetTy,
"InstantiateAtCallOpCompute")) {
1992 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
1994 "result type mismatch: due to struct instantiation, expected type ", newRetTy,
1995 ", but found ", oldRetTy
1999 LLVM_DEBUG(llvm::dbgs() <<
"[InstantiateAtCallOpCompute] replaced " << op);
2001 rewriter, op, TypeRange {newRetTy}, op.
getCallee(),
2002 AffineMapFolder::getConvertedMapOpGroups(out), out.dimsPerGroup, op.
getArgOperands()
2005 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
2012 inline LogicalResult instantiateViaTargetType(
2013 const AffineMapFolder::Input &in, AffineMapFolder::Output &out,
2014 OperandRange::type_range callArgTypes,
FuncDefOp targetFunc
2019 assert(in.paramsOfStructTy.size() == targetResTyParams.size());
2021 if (llvm::all_of(in.paramsOfStructTy, isConcreteAttr<>)) {
2027 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
2029 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']' <<
" target func arg types: "
2031 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
2033 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
2040 assert(unifies &&
"should have been checked by verifiers");
2043 llvm::dbgs() <<
'[' << __FUNCTION__ <<
']'
2052 SmallVector<Attribute> newReturnStructParams = llvm::map_to_vector(
2053 llvm::zip_equal(targetResTyParams.getValue(), in.paramsOfStructTy),
2054 [&unifications](std::tuple<Attribute, Attribute> p) {
2055 Attribute fromCall = std::get<1>(p);
2058 if (!isConcreteAttr(fromCall)) {
2059 Attribute fromTgt = std::get<0>(p);
2061 llvm::dbgs() <<
"[instantiateViaTargetType] fromCall = " << fromCall <<
'\n';
2062 llvm::dbgs() <<
"[instantiateViaTargetType] fromTgt = " << fromTgt <<
'\n';
2064 assert(llvm::isa<SymbolRefAttr>(fromTgt));
2065 auto it = unifications.find(std::make_pair(llvm::cast<SymbolRefAttr>(fromTgt), Side::LHS));
2066 if (it != unifications.end()) {
2067 Attribute unifiedAttr = it->second;
2069 llvm::dbgs() <<
"[instantiateViaTargetType] unifiedAttr = " << unifiedAttr <<
'\n';
2071 if (unifiedAttr && isConcreteAttr<false>(unifiedAttr)) {
2080 out.paramsOfStructTy = newReturnStructParams;
2081 assert(out.paramsOfStructTy.size() == in.paramsOfStructTy.size() &&
"post-condition");
2082 assert(out.mapOpGroups.empty() &&
"post-condition");
2083 assert(out.dimsPerGroup.empty() &&
"post-condition");
2088LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
2089 MLIRContext *ctx = modOp.getContext();
2090 RewritePatternSet patterns(ctx);
2092 InstantiateAtCreateArrayOp,
2093 InstantiateAtCallOpCompute
2096 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
2104class UpdateNewArrayElemFromWrite final :
public OpRewritePattern<CreateArrayOp> {
2105 ConversionTracker &tracker_;
2108 UpdateNewArrayElemFromWrite(MLIRContext *ctx, ConversionTracker &tracker)
2109 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2111 LogicalResult matchAndRewrite(
CreateArrayOp op, PatternRewriter &rewriter)
const override {
2113 ArrayType createResultType = dyn_cast<ArrayType>(createResult.getType());
2114 assert(createResultType &&
"CreateArrayOp must produce ArrayType");
2119 Type newResultElemType =
nullptr;
2120 for (Operation *user : createResult.getUsers()) {
2121 if (
WriteArrayOp writeOp = dyn_cast<WriteArrayOp>(user)) {
2122 if (writeOp.getArrRef() != createResult) {
2125 Type writeRValueType = writeOp.getRvalue().getType();
2126 if (writeRValueType == oldResultElemType) {
2129 if (newResultElemType && newResultElemType != writeRValueType) {
2132 <<
"[UpdateNewArrayElemFromWrite] multiple possible element types for CreateArrayOp "
2133 << newResultElemType <<
" vs " << writeRValueType <<
'\n'
2137 newResultElemType = writeRValueType;
2140 if (!newResultElemType) {
2144 if (!tracker_.isLegalConversion(
2145 oldResultElemType, newResultElemType,
"UpdateNewArrayElemFromWrite"
2150 rewriter.modifyOpInPlace(op, [&createResult, &newType]() { createResult.setType(newType); });
2152 llvm::dbgs() <<
"[UpdateNewArrayElemFromWrite] updated result type of " << op <<
'\n'
2160LogicalResult updateArrayElemFromArrAccessOp(
2162 PatternRewriter &rewriter
2169 if (oldArrType == newArrType ||
2170 !tracker.isLegalConversion(oldArrType, newArrType,
"updateArrayElemFromArrAccessOp")) {
2173 rewriter.modifyOpInPlace(op, [&op, &newArrType]() { op.
getArrRef().setType(newArrType); });
2175 llvm::dbgs() <<
"[updateArrayElemFromArrAccessOp] updated base array type in " << op <<
'\n'
2182class UpdateArrayElemFromArrWrite final :
public OpRewritePattern<WriteArrayOp> {
2183 ConversionTracker &tracker_;
2186 UpdateArrayElemFromArrWrite(MLIRContext *ctx, ConversionTracker &tracker)
2187 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2189 LogicalResult matchAndRewrite(
WriteArrayOp op, PatternRewriter &rewriter)
const override {
2190 return updateArrayElemFromArrAccessOp(op, op.
getRvalue().getType(), tracker_, rewriter);
2194class UpdateArrayElemFromArrRead final :
public OpRewritePattern<ReadArrayOp> {
2195 ConversionTracker &tracker_;
2198 UpdateArrayElemFromArrRead(MLIRContext *ctx, ConversionTracker &tracker)
2199 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2201 LogicalResult matchAndRewrite(
ReadArrayOp op, PatternRewriter &rewriter)
const override {
2202 return updateArrayElemFromArrAccessOp(op, op.
getResult().getType(), tracker_, rewriter);
2207class UpdateMemberDefTypeFromWrite final :
public OpRewritePattern<MemberDefOp> {
2208 ConversionTracker &tracker_;
2211 UpdateMemberDefTypeFromWrite(MLIRContext *ctx, ConversionTracker &tracker)
2212 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2214 LogicalResult matchAndRewrite(
MemberDefOp op, PatternRewriter &rewriter)
const override {
2217 assert(parentRes &&
"MemberDefOp parent is always StructDefOp");
2221 Type newType =
nullptr;
2223 std::optional<Location> newTypeLoc = std::nullopt;
2224 for (SymbolTable::SymbolUse symUse : memberUsers.value()) {
2225 if (
MemberWriteOp writeOp = llvm::dyn_cast<MemberWriteOp>(symUse.getUser())) {
2226 Type writeToType = writeOp.getVal().getType();
2227 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateMemberDefTypeFromWrite] checking " << writeOp <<
'\n');
2230 newType = writeToType;
2231 newTypeLoc = writeOp.getLoc();
2232 }
else if (writeToType != newType) {
2238 if (!tracker_.isLegalConversion(writeToType, newType,
"UpdateMemberDefTypeFromWrite")) {
2239 if (tracker_.isLegalConversion(
2240 newType, writeToType,
"UpdateMemberDefTypeFromWrite"
2243 newType = writeToType;
2244 newTypeLoc = writeOp.getLoc();
2247 return rewriter.notifyMatchFailure(op, [&](Diagnostic &diag) {
2251 "' with different value types"
2254 diag.attachNote(newTypeLoc).append(
"type written here is ", newType);
2256 diag.attachNote(writeOp.getLoc()).append(
"type written here is ", writeToType);
2264 if (!newType || newType == op.
getType()) {
2267 if (!tracker_.isLegalConversion(op.
getType(), newType,
"UpdateMemberDefTypeFromWrite")) {
2270 rewriter.modifyOpInPlace(op, [&op, &newType]() { op.
setType(newType); });
2271 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateMemberDefTypeFromWrite] updated type of " << op <<
'\n');
2278SmallVector<std::unique_ptr<Region>> moveRegions(Operation *op) {
2279 SmallVector<std::unique_ptr<Region>> newRegions;
2280 for (Region ®ion : op->getRegions()) {
2281 auto newRegion = std::make_unique<Region>();
2282 newRegion->takeBody(region);
2283 newRegions.push_back(std::move(newRegion));
2292class UpdateInferredResultTypes final :
public OpTraitRewritePattern<OpTrait::InferTypeOpAdaptor> {
2293 ConversionTracker &tracker_;
2296 UpdateInferredResultTypes(MLIRContext *ctx, ConversionTracker &tracker)
2297 : OpTraitRewritePattern(ctx, 6), tracker_(tracker) {}
2299 LogicalResult matchAndRewrite(Operation *op, PatternRewriter &rewriter)
const override {
2300 SmallVector<Type, 1> inferredResultTypes;
2301 InferTypeOpInterface retTypeFn = llvm::cast<InferTypeOpInterface>(op);
2302 LogicalResult result = retTypeFn.inferReturnTypes(
2303 op->getContext(), op->getLoc(), op->getOperands(), op->getRawDictionaryAttrs(),
2304 op->getPropertiesStorage(), op->getRegions(), inferredResultTypes
2306 if (failed(result)) {
2309 if (op->getResultTypes() == inferredResultTypes) {
2312 if (!tracker_.areLegalConversions(
2313 op->getResultTypes(), inferredResultTypes,
"UpdateInferredResultTypes"
2319 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateInferredResultTypes] replaced " << *op);
2320 SmallVector<std::unique_ptr<Region>> newRegions = moveRegions(op);
2321 Operation *newOp = rewriter.create(
2322 op->getLoc(), op->getName().getIdentifier(), op->getOperands(), inferredResultTypes,
2323 op->getAttrs(), op->getSuccessors(), newRegions
2325 rewriter.replaceOp(op, newOp);
2326 LLVM_DEBUG(llvm::dbgs() <<
" with " << *newOp <<
'\n');
2332class UpdateFuncTypeFromReturn final :
public OpRewritePattern<FuncDefOp> {
2333 ConversionTracker &tracker_;
2336 UpdateFuncTypeFromReturn(MLIRContext *ctx, ConversionTracker &tracker)
2337 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2339 LogicalResult matchAndRewrite(
FuncDefOp op, PatternRewriter &rewriter)
const override {
2340 Region &body = op.getFunctionBody();
2344 ReturnOp retOp = llvm::dyn_cast<ReturnOp>(body.back().getTerminator());
2345 assert(retOp &&
"final op in body region must be return");
2346 OperandRange::type_range tyFromReturnOp = retOp.
getOperands().getTypes();
2349 if (oldFuncTy.getResults() == tyFromReturnOp) {
2352 if (!tracker_.areLegalConversions(
2353 oldFuncTy.getResults(), tyFromReturnOp,
"UpdateFuncTypeFromReturn"
2358 rewriter.modifyOpInPlace(op, [&]() {
2359 op.
setFunctionType(rewriter.getFunctionType(oldFuncTy.getInputs(), tyFromReturnOp));
2362 llvm::dbgs() <<
"[UpdateFuncTypeFromReturn] changed " << op.
getSymName() <<
" from "
2373class UpdateFreeFuncCallOpTypes final :
public OpRewritePattern<CallOp> {
2374 ConversionTracker &tracker_;
2377 UpdateFreeFuncCallOpTypes(MLIRContext *ctx, ConversionTracker &tracker)
2378 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2380 LogicalResult matchAndRewrite(
CallOp op, PatternRewriter &rewriter)
const override {
2381 if (calleeReferencesTemplateParam(op)) {
2384 SymbolTableCollection tables;
2386 if (failed(lookupRes)) {
2389 FuncDefOp targetFunc = lookupRes->get();
2394 if (op.getResultTypes() == targetFunc.
getFunctionType().getResults()) {
2397 if (!tracker_.areLegalConversions(
2399 "UpdateFreeFuncCallOpTypes"
2404 LLVM_DEBUG(llvm::dbgs() <<
"[UpdateFreeFuncCallOpTypes] replaced " << op);
2407 LLVM_DEBUG(llvm::dbgs() <<
" with " << newOp <<
'\n');
2414LogicalResult updateMemberRefValFromMemberDef(
2417 SymbolTableCollection tables;
2422 Type oldResultType = op.
getVal().getType();
2423 Type newResultType = def->get().getType();
2424 if (oldResultType == newResultType ||
2425 !tracker.isLegalConversion(oldResultType, newResultType,
"updateMemberRefValFromMemberDef")) {
2428 rewriter.modifyOpInPlace(op, [&op, &newResultType]() { op.
getVal().setType(newResultType); });
2430 llvm::dbgs() <<
"[updateMemberRefValFromMemberDef] updated value type in " << op <<
'\n'
2438class UpdateMemberReadValFromDef final :
public OpRewritePattern<MemberReadOp> {
2439 ConversionTracker &tracker_;
2442 UpdateMemberReadValFromDef(MLIRContext *ctx, ConversionTracker &tracker)
2443 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2445 LogicalResult matchAndRewrite(
MemberReadOp op, PatternRewriter &rewriter)
const override {
2446 return updateMemberRefValFromMemberDef(op, tracker_, rewriter);
2451class UpdateMemberWriteValFromDef final :
public OpRewritePattern<MemberWriteOp> {
2452 ConversionTracker &tracker_;
2455 UpdateMemberWriteValFromDef(MLIRContext *ctx, ConversionTracker &tracker)
2456 : OpRewritePattern(ctx, 3), tracker_(tracker) {}
2458 LogicalResult matchAndRewrite(
MemberWriteOp op, PatternRewriter &rewriter)
const override {
2459 return updateMemberRefValFromMemberDef(op, tracker_, rewriter);
2463LogicalResult run(ModuleOp modOp, ConversionTracker &tracker) {
2464 MLIRContext *ctx = modOp.getContext();
2465 RewritePatternSet patterns(ctx);
2470 UpdateInferredResultTypes,
2472 UpdateFreeFuncCallOpTypes,
2473 UpdateFuncTypeFromReturn,
2474 UpdateNewArrayElemFromWrite,
2475 UpdateArrayElemFromArrRead,
2476 UpdateArrayElemFromArrWrite,
2477 UpdateMemberDefTypeFromWrite,
2478 UpdateMemberReadValFromDef,
2479 UpdateMemberWriteValFromDef
2482 return applyAndFoldGreedily(modOp, tracker, std::move(patterns));
2493 static bool hasTemplateSymbolBindings(Operation *op) {
2494 if (
StructDefOp sdef = llvm::dyn_cast<StructDefOp>(op)) {
2495 return sdef.hasTemplateSymbolBindings();
2497 if (llvm::isa<function::FuncDefOp>(op)) {
2508 LogicalResult eraseUnreachableFrom(ArrayRef<SymbolOpInterface> keep) {
2510 SetVector<SymbolOpInterface> roots(keep.begin(), keep.end());
2517 DenseSet<Operation *> defsToKeep;
2518 llvm::df_iterator_default_set<const SymbolUseGraphNode *> symbolsToKeep;
2519 for (
size_t i = 0; i < roots.size(); ++i) {
2520 SymbolOpInterface keepRoot = roots[i];
2521 LLVM_DEBUG({ llvm::dbgs() <<
"[EraseUnreachable] root: " << keepRoot <<
'\n'; });
2523 assert(keepRootNode &&
"every symbol def must be in the def tree");
2524 for (
const SymbolDefTreeNode *reachableDefNode : llvm::depth_first(keepRootNode)) {
2526 llvm::dbgs() <<
"[EraseUnreachable] can reach: " << reachableDefNode->getOp() <<
'\n';
2528 if (SymbolOpInterface reachableDef = reachableDefNode->getOp()) {
2530 defsToKeep.insert(reachableDef.getOperation());
2536 if (
const SymbolUseGraphNode *useGraphNodeForDef = useGraph.lookupNode(reachableDef)) {
2538 depth_first_ext(useGraphNodeForDef, symbolsToKeep)) {
2540 llvm::dbgs() <<
"[EraseUnreachable] uses symbol: "
2541 << usedSymbolNode->getSymbolPath() <<
'\n';
2545 if (usedSymbolNode->isTemplateSymbolBinding()) {
2550 auto lookupRes = usedSymbolNode->lookupSymbol(tables);
2551 if (failed(lookupRes)) {
2552 LLVM_DEBUG(useGraph.dumpToDotFile());
2556 if (lookupRes->viaInclude()) {
2559 Operation *usedOp = lookupRes->get();
2561 SymbolOpInterface asSymbol = llvm::cast<SymbolOpInterface>(usedOp);
2562 bool insertRes = roots.insert(asSymbol);
2566 llvm::dbgs() <<
"[EraseUnreachable] found another root: " << asSymbol <<
'\n';
2576 SmallVector<SymbolOpInterface> toErase;
2577 rootMod.walk([
this, &defsToKeep, &symbolsToKeep, &toErase](Operation *op) {
2581 SymbolOpInterface symOp = llvm::cast<SymbolOpInterface>(op);
2583 if (!n || !symbolsToKeep.contains(n)) {
2584 LLVM_DEBUG(llvm::dbgs() <<
"[EraseUnreachable] removing: " << symOp.getNameAttr() <<
'\n');
2585 toErase.push_back(symOp);
2588 for (SymbolOpInterface symOp : toErase) {
2599 using Base = FlatteningPassBase<PassImpl>;
2608 void runOnOperation()
override {
2609 ModuleOp modOp = getOperation();
2610 if (failed(runOn(modOp))) {
2613 llvm::dbgs() <<
"=====================================================================\n";
2614 llvm::dbgs() <<
" Dumping module after failure of pass " <<
DEBUG_TYPE <<
'\n';
2615 modOp.print(llvm::dbgs(), OpPrintingFlags().assumeVerified());
2616 llvm::dbgs() <<
"=====================================================================\n";
2618 signalPassFailure();
2622 inline LogicalResult runOn(ModuleOp modOp) {
2628 if (effectiveCleanupMode == FlatteningCleanupMode::MainAsRoot) {
2629 if (failed(eraseUnreachableFromMainStruct(modOp))) {
2637 OpPassManager universalCleanup(ModuleOp::getOperationName());
2642 if (failed(runPipeline(universalCleanup, modOp))) {
2646 ConversionTracker tracker;
2647 if (failed(Step1_InstantiateStructs::instantiateMainStruct(modOp, tracker))) {
2648 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating the main struct\n";
2652 unsigned loopCount = 0;
2655 if (loopCount > iterationLimit) {
2656 llvm::errs() <<
DEBUG_TYPE <<
" exceeded the limit of " << iterationLimit
2657 <<
" iterations!\n";
2660 tracker.resetModifiedFlag();
2663 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2664 <<
")] Running step 1: struct instantiation\n";
2669 if (failed(Step1_InstantiateStructs::run(modOp, tracker))) {
2670 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating structs in templates\n";
2674 if (failed(Step2_InstantiateFunctions::run(modOp, tracker))) {
2675 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating functions in templates\n";
2680 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2681 <<
")] Running step 2: loop unrolling\n";
2684 if (failed(Step3_Unroll::run(modOp, tracker))) {
2685 llvm::errs() <<
DEBUG_TYPE <<
" failed while unrolling loops\n";
2690 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2691 <<
")] Running step 3: affine maps instantiation\n";
2694 if (failed(Step4_InstantiateAffineMaps::run(modOp, tracker))) {
2695 llvm::errs() <<
DEBUG_TYPE <<
" failed while instantiating `affine_map` parameters\n";
2700 llvm::dbgs() <<
"[FlatteningPass(count=" << loopCount
2701 <<
")] Running step 4: type propagation\n";
2704 if (failed(Step5_PropagateTypes::run(modOp, tracker))) {
2705 llvm::errs() <<
DEBUG_TYPE <<
" failed while propagating instantiated types\n";
2709 LLVM_DEBUG(
if (tracker.isModified()) {
2710 llvm::dbgs() <<
"=====================================================================\n";
2711 llvm::dbgs() <<
" Dumping module between iterations of " << DEBUG_TYPE <<
'\n';
2712 modOp.print(llvm::dbgs(), OpPrintingFlags().assumeVerified());
2713 llvm::dbgs() <<
"=====================================================================\n";
2715 }
while (tracker.isModified());
2717 tracker.clearPartialFuncInstantiations();
2720 if (failed(cleanupSwitch(modOp, tracker))) {
2724 if (failed(runPipeline(universalCleanup, modOp))) {
2728 OpPassManager allocationCleanup(ModuleOp::getOperationName());
2730 RemoveUnusedDiscardableAllocationsPassOptions {
2734 return runPipeline(allocationCleanup, modOp);
2738 LogicalResult cleanupSwitch(ModuleOp modOp,
const ConversionTracker &tracker) {
2740 LLVM_DEBUG({ llvm::dbgs() <<
"[FlatteningPass] Running step 5: cleanup "; });
2741 switch (effectiveCleanupMode) {
2742 case FlatteningCleanupMode::MainAsRoot:
2743 LLVM_DEBUG(llvm::dbgs() <<
"(main as root mode)\n");
2744 return eraseUnreachableFromMainStruct(modOp,
false);
2745 case FlatteningCleanupMode::ConcreteAsRoot:
2746 LLVM_DEBUG(llvm::dbgs() <<
"(concrete definitions mode)\n");
2747 return eraseUnreachableFromConcreteDefinitions(modOp);
2748 case FlatteningCleanupMode::Preimage:
2749 LLVM_DEBUG(llvm::dbgs() <<
"(preimage mode)\n");
2750 return erasePreimageOfInstantiations(modOp, tracker);
2751 case FlatteningCleanupMode::Unspecified:
2753 LLVM_DEBUG(llvm::dbgs() <<
"(disabled)\n");
2759 LogicalResult erasePreimageOfInstantiations(ModuleOp rootMod,
const ConversionTracker &tracker) {
2764 FromEraseSet cleaner(
2765 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>(),
2766 tracker.getInstantiatedDefinitionNames()
2768 LogicalResult res = cleaner.eraseUnusedDefinitions();
2769 if (succeeded(res)) {
2770 LLVM_DEBUG(llvm::dbgs() <<
"[Cleanup(preimage)] success\n";);
2772 const SymbolUseGraph *useGraph =
nullptr;
2773 rootMod->walk([
this, &cleaner, &useGraph](Operation *walkedOp) {
2774 SymbolOpInterface op = llvm::dyn_cast<SymbolOpInterface>(walkedOp);
2775 if (!op || !cleaner.getTryToEraseSet().contains(op)) {
2780 useGraph = &getAnalysis<SymbolUseGraph>();
2783 if (useGraph->lookupNode(op)->hasPredecessor()) {
2784 op.emitWarning(
"Parameterized definition still has uses!").report();
2788 LLVM_DEBUG(llvm::dbgs() <<
"[Cleanup(preimage)] failed\n";);
2793 LogicalResult eraseUnreachableFromConcreteDefinitions(ModuleOp rootMod) {
2794 SmallVector<SymbolOpInterface> roots;
2795 rootMod.walk([&roots](Operation *op) {
2797 roots.push_back(llvm::cast<SymbolOpInterface>(op));
2801 Step6_Cleanup::FromKeepSet cleaner(
2802 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>()
2804 return cleaner.eraseUnreachableFrom(roots);
2807 LogicalResult eraseUnreachableFromMainStruct(ModuleOp rootMod,
bool emitWarning =
true) {
2808 Step6_Cleanup::FromKeepSet cleaner(
2809 rootMod, getAnalysis<SymbolDefTree>(), getAnalysis<SymbolUseGraph>()
2811 FailureOr<SymbolLookupResult<StructDefOp>> mainOpt =
2813 if (failed(mainOpt)) {
2816 SymbolLookupResult<StructDefOp>
main = mainOpt.value();
2817 if (emitWarning && !
main) {
2820 rootMod.emitWarning()
2822 "using option '", cleanupMode.getArgStr(),
'=',
2825 "\" attribute on the top-level module may remove all cleanup-candidate definitions!"
2829 SmallVector<SymbolOpInterface> roots;
2831 roots.push_back(*
main);
2833 return cleaner.eraseUnreachableFrom(roots);
Common private implementation for poly dialect passes.
This file defines methods symbol lookup across LLZK operations and included files.
::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<::llzk::array::ArrayType > getArrRef()
::mlir::TypedValue<::llzk::array::ArrayType > getArrRef()
::mlir::TypedValue<::mlir::Type > getRvalue()
static constexpr ::llvm::StringLiteral getOperationName()
void setType(::mlir::Type attrValue)
::std::optional<::mlir::Attribute > getTableOffset()
void setTableOffsetAttr(::mlir::Attribute attr)
::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.
static constexpr ::llvm::StringLiteral getOperationName()
::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()
::mlir::FunctionType getTypeSignature()
Return the FunctionType inferred from the arg operands and result types of this CallOp.
void setTemplateParamsAttr(::mlir::ArrayAttr attr)
::mlir::Operation::operand_range getArgOperands()
::mlir::ArrayAttr getTemplateParamsAttr()
::mlir::OperandRangeRange getMapOperands()
void setCalleeAttr(::mlir::SymbolRefAttr attr)
::mlir::FailureOr< UnificationMap > unifyTypeSignature(::mlir::FunctionType other)
Attempt type unfication between the inferred FunctionType from this CallOp (as LHS) and the given Fun...
::mlir::LogicalResult verifyTemplateParamsMatchInferred(::llvm::iterator_range<::mlir::Region::op_iterator<::llzk::polymorphic::TemplateParamOp > > targetParamDefs, const UnificationMap &unifications)
Verify that each template parameter value provided in this CallOp is consistent with the value inferr...
::mlir::LogicalResult verifyTemplateParamCompatibility(::mlir::Attribute paramFromCallOp, ::llzk::polymorphic::TemplateParamOp targetParam)
Check type compatibility of the given template parameter value from this CallOp against the declared ...
::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::FlatSymbolRefAttr getConstNameAttr()
::mlir::StringAttr getSymNameAttr()
::mlir::Region & getInitializerRegion()
::llvm::StringRef getSymName()
::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()
::mlir::FlatSymbolRefAttr getNameRef() const
Shared state for post-instantiation cleanup helpers.
CleanupBase(mlir::ModuleOp root, const SymbolDefTree &symDefTree, const SymbolUseGraph &symUseGraph)
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...
component::StructType getStructTypeWithParams(mlir::SymbolRefAttr nameRef, mlir::ArrayAttr params)
Build a struct type while representing an empty parameter list as absent.
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...
mlir::ConversionTarget newConverterDefinedTarget(mlir::TypeConverter &tyConv, mlir::MLIRContext *ctx, AdditionalChecks &&...checks)
Return a new ConversionTarget allowing all LLZK-required dialects and defining Op legality based on t...
FailureOr< InstantiationLayout > buildInstantiationLayout(TemplateOp parentTemplate, ArrayAttr callParams, const DenseMap< Attribute, Attribute > ¶mNameToConcrete)
void setInstantiationNamePattern(TemplateOp templateOp, ArrayAttr namePattern)
bool isErasableDefinition(mlir::Operation *op)
Return true iff op is a cleanup candidate.
std::unique_ptr<::mlir::Pass > createEmptyTemplateRemovalPass()
::llvm::StringRef stringifyFlatteningCleanupMode(FlatteningCleanupMode val)
OpClass replaceOpWithNewOp(Rewriter &rewriter, mlir::Operation *op, Args &&...args)
Wrapper for PatternRewriter::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.
std::string stringWithoutType(mlir::Attribute a)
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)
ArrayType flattenArrayElementType(ArrayType outerArrTy, Type elementType)
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.
mlir::SymbolRefAttr asSymbolRefAttr(mlir::StringAttr root, mlir::SymbolRefAttr tail)
Build a SymbolRefAttr that prepends tail with root, i.e., root::tail.
int64_t fromAPInt(const llvm::APInt &i)
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.