//===- SubElementInterfaces.cpp - Attr and Type SubElement Interfaces -----===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "mlir/IR/SubElementInterfaces.h" #include "llvm/ADT/DenseSet.h" using namespace mlir; //===----------------------------------------------------------------------===// // SubElementInterface //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // WalkSubElements template static void walkSubElementsImpl(InterfaceT interface, function_ref walkAttrsFn, function_ref walkTypesFn, DenseSet &visitedAttrs, DenseSet &visitedTypes) { interface.walkImmediateSubElements( [&](Attribute attr) { // Guard against potentially null inputs. This removes the need for the // derived attribute/type to do it. if (!attr) return; // Avoid infinite recursion when visiting sub attributes later, if this // is a mutable attribute. if (LLVM_UNLIKELY(attr.hasTrait())) { if (!visitedAttrs.insert(attr).second) return; } // Walk any sub elements first. if (auto interface = attr.dyn_cast()) walkSubElementsImpl(interface, walkAttrsFn, walkTypesFn, visitedAttrs, visitedTypes); // Walk this attribute. walkAttrsFn(attr); }, [&](Type type) { // Guard against potentially null inputs. This removes the need for the // derived attribute/type to do it. if (!type) return; // Avoid infinite recursion when visiting sub types later, if this // is a mutable type. if (LLVM_UNLIKELY(type.hasTrait())) { if (!visitedTypes.insert(type).second) return; } // Walk any sub elements first. if (auto interface = type.dyn_cast()) walkSubElementsImpl(interface, walkAttrsFn, walkTypesFn, visitedAttrs, visitedTypes); // Walk this type. walkTypesFn(type); }); } void SubElementAttrInterface::walkSubElements( function_ref walkAttrsFn, function_ref walkTypesFn) { assert(walkAttrsFn && walkTypesFn && "expected valid walk functions"); DenseSet visitedAttrs; DenseSet visitedTypes; walkSubElementsImpl(*this, walkAttrsFn, walkTypesFn, visitedAttrs, visitedTypes); } void SubElementTypeInterface::walkSubElements( function_ref walkAttrsFn, function_ref walkTypesFn) { assert(walkAttrsFn && walkTypesFn && "expected valid walk functions"); DenseSet visitedAttrs; DenseSet visitedTypes; walkSubElementsImpl(*this, walkAttrsFn, walkTypesFn, visitedAttrs, visitedTypes); } //===----------------------------------------------------------------------===// // ReplaceSubElements /// Return if the given element is mutable. static bool isMutable(Attribute attr) { return attr.hasTrait(); } static bool isMutable(Type type) { return type.hasTrait(); } template static void updateSubElementImpl(T element, function_ref walkFn, DenseMap &visited, SmallVectorImpl &newElements, FailureOr &changed, ReplaceSubElementFnT &&replaceSubElementFn) { // Bail early if we failed at any point. if (failed(changed)) return; newElements.push_back(element); // Guard against potentially null inputs. We always map null to null. if (!element) return; // Check for an existing mapping for this element, and walk it if we haven't // yet. T &mappedElement = visited[element]; if (!mappedElement) { // Try walking this element. if (!(mappedElement = walkFn(element))) { changed = failure(); return; } // Handle replacing sub-elements if this element is also a container. if (auto interface = mappedElement.template dyn_cast()) { if (!(mappedElement = replaceSubElementFn(interface))) { changed = failure(); return; } } } // Update to the mapped element. if (mappedElement != element) { newElements.back() = mappedElement; changed = true; } } template static typename InterfaceT::ValueType replaceSubElementsImpl(InterfaceT interface, function_ref walkAttrsFn, function_ref walkTypesFn, DenseMap &visitedAttrs, DenseMap &visitedTypes) { // Walk the current sub-elements, replacing them as necessary. SmallVector newAttrs; SmallVector newTypes; FailureOr changed = false; auto replaceSubElementFn = [&](auto subInterface) { return replaceSubElementsImpl(subInterface, walkAttrsFn, walkTypesFn, visitedAttrs, visitedTypes); }; interface.walkImmediateSubElements( [&](Attribute element) { updateSubElementImpl( element, walkAttrsFn, visitedAttrs, newAttrs, changed, replaceSubElementFn); }, [&](Type element) { updateSubElementImpl( element, walkTypesFn, visitedTypes, newTypes, changed, replaceSubElementFn); }); if (failed(changed)) return {}; // If the sub-elements didn't change, just return the original value. if (!*changed) return interface; // If this element is mutable, we don't support changing its sub elements, the // sub element walk doesn't give us a valid ordering for what we need here. If // we want to support mutable elements, we'll need something more. if (isMutable(interface)) return {}; // Use the new elements during the replacement. return interface.replaceImmediateSubElements(newAttrs, newTypes); } Attribute SubElementAttrInterface::replaceSubElements( function_ref replaceAttrFn, function_ref replaceTypeFn) { assert(replaceAttrFn && replaceTypeFn && "expected valid replace functions"); DenseMap visitedAttrs; DenseMap visitedTypes; return replaceSubElementsImpl(*this, replaceAttrFn, replaceTypeFn, visitedAttrs, visitedTypes); } Type SubElementTypeInterface::replaceSubElements( function_ref replaceAttrFn, function_ref replaceTypeFn) { assert(replaceAttrFn && replaceTypeFn && "expected valid replace functions"); DenseMap visitedAttrs; DenseMap visitedTypes; return replaceSubElementsImpl(*this, replaceAttrFn, replaceTypeFn, visitedAttrs, visitedTypes); } //===----------------------------------------------------------------------===// // SubElementInterface Tablegen definitions //===----------------------------------------------------------------------===// #include "mlir/IR/SubElementAttrInterfaces.cpp.inc" #include "mlir/IR/SubElementTypeInterfaces.cpp.inc"