[flang-commits] [flang] [mlir] [mlir][Transforms] Support 1:N mappings in `ConversionValueMapping` (PR #116524)
Markus Böck via flang-commits
flang-commits at lists.llvm.org
Fri Jan 3 05:57:13 PST 2025
================
@@ -63,80 +113,163 @@ static OpBuilder::InsertPoint computeInsertPoint(Value value) {
return OpBuilder::InsertPoint(insertBlock, insertPt);
}
+/// Helper function that computes an insertion point where the given values are
+/// defined and can be used without a dominance violation.
+static OpBuilder::InsertPoint computeInsertPoint(ArrayRef<Value> vals) {
+ assert(!vals.empty() && "expected at least one value");
+ OpBuilder::InsertPoint pt = computeInsertPoint(vals.front());
+ for (Value v : vals.drop_front())
+ pt = chooseLaterInsertPoint(pt, computeInsertPoint(v));
+ return pt;
+}
+
//===----------------------------------------------------------------------===//
// ConversionValueMapping
//===----------------------------------------------------------------------===//
+/// A vector of SSA values, optimized for the most common case of a single
+/// value.
+using ValueVector = SmallVector<Value, 1>;
+
namespace {
+
+/// Helper class to make it possible to use `ValueVector` as a key in DenseMap.
+struct ValueVectorMapInfo {
+ static ValueVector getEmptyKey() { return ValueVector{}; }
+ static ValueVector getTombstoneKey() { return ValueVector{}; }
+ static ::llvm::hash_code getHashValue(const ValueVector &val) {
+ return ::llvm::hash_combine_range(val.begin(), val.end());
+ }
+ static bool isEqual(const ValueVector &LHS, const ValueVector &RHS) {
+ return LHS == RHS;
+ }
+};
+
/// This class wraps a IRMapping to provide recursive lookup
/// functionality, i.e. we will traverse if the mapped value also has a mapping.
struct ConversionValueMapping {
/// Return "true" if an SSA value is mapped to the given value. May return
/// false positives.
bool isMappedTo(Value value) const { return mappedTo.contains(value); }
- /// Lookup the most recently mapped value with the desired type in the
+ /// Lookup the most recently mapped values with the desired types in the
/// mapping.
///
/// Special cases:
- /// - If the desired type is "null", simply return the most recently mapped
+ /// - If the desired type range is empty, simply return the most recently
+ /// mapped values.
+ /// - If there is no mapping to the desired types, also return the most
+ /// recently mapped values.
+ /// - If there is no mapping for the given values at all, return the given
/// value.
- /// - If there is no mapping to the desired type, also return the most
- /// recently mapped value.
- /// - If there is no mapping for the given value at all, return the given
- /// value.
- Value lookupOrDefault(Value from, Type desiredType = nullptr) const;
+ ValueVector lookupOrDefault(Value from, TypeRange desiredTypes = {}) const;
+
+ /// Lookup the given value within the map, or return an empty vector if the
+ /// value is not mapped. If it is mapped, this follows the same behavior
+ /// as `lookupOrDefault`.
+ ValueVector lookupOrNull(Value from, TypeRange desiredTypes = {}) const;
- /// Lookup a mapped value within the map, or return null if a mapping does not
- /// exist. If a mapping exists, this follows the same behavior of
- /// `lookupOrDefault`.
- Value lookupOrNull(Value from, Type desiredType = nullptr) const;
+ template <typename T>
+ struct IsValueVector : std::is_same<std::decay_t<T>, ValueVector> {};
- /// Map a value to the one provided.
- void map(Value oldVal, Value newVal) {
+ /// Map a value vector to the one provided.
+ template <typename OldVal, typename NewVal>
+ std::enable_if_t<IsValueVector<OldVal>::value && IsValueVector<NewVal>::value>
+ map(OldVal &&oldVal, NewVal &&newVal) {
LLVM_DEBUG({
- for (Value it = newVal; it; it = mapping.lookupOrNull(it))
- assert(it != oldVal && "inserting cyclic mapping");
+ ValueVector next(newVal);
+ while (true) {
+ assert(next != oldVal && "inserting cyclic mapping");
+ auto it = mapping.find(next);
+ if (it == mapping.end())
+ break;
+ next = it->second;
+ }
});
- mapping.map(oldVal, newVal);
- mappedTo.insert(newVal);
+ for (Value v : newVal)
+ mappedTo.insert(v);
+
+ mapping[std::forward<OldVal>(oldVal)] = std::forward<NewVal>(newVal);
}
- /// Drop the last mapping for the given value.
- void erase(Value value) { mapping.erase(value); }
+ /// Map a value vector or single value to the one provided.
+ template <typename OldVal, typename NewVal>
+ std::enable_if_t<!IsValueVector<OldVal>::value ||
+ !IsValueVector<NewVal>::value>
+ map(OldVal &&oldVal, NewVal &&newVal) {
+ if constexpr (IsValueVector<OldVal>{}) {
+ map(std::forward<OldVal>(oldVal), ValueVector{newVal});
+ } else if constexpr (IsValueVector<NewVal>{}) {
+ map(ValueVector{oldVal}, std::forward<NewVal>(newVal));
+ } else {
+ map(ValueVector{oldVal}, ValueVector{newVal});
+ }
+ }
+
+ /// Drop the last mapping for the given values.
+ void erase(const ValueVector &value) { mapping.erase(value); }
private:
/// Current value mappings.
- IRMapping mapping;
+ DenseMap<ValueVector, ValueVector, ValueVectorMapInfo> mapping;
/// All SSA values that are mapped to. May contain false positives.
DenseSet<Value> mappedTo;
};
} // namespace
-Value ConversionValueMapping::lookupOrDefault(Value from,
- Type desiredType) const {
- // Try to find the deepest value that has the desired type. If there is no
- // such value, simply return the deepest value.
- Value desiredValue;
+ValueVector
+ConversionValueMapping::lookupOrDefault(Value from,
+ TypeRange desiredTypes) const {
+ // Try to find the deepest values that have the desired types. If there is no
+ // such mapping, simply return the deepest values.
+ ValueVector desiredValue;
+ ValueVector current{from};
do {
- if (!desiredType || from.getType() == desiredType)
- desiredValue = from;
+ // Store the current value if the types match.
+ if (TypeRange(current) == desiredTypes)
+ desiredValue = current;
+
+ // If possible, Replace each value with (one or multiple) mapped values.
+ ValueVector next;
+ for (Value v : current) {
+ auto it = mapping.find({v});
+ if (it != mapping.end()) {
+ llvm::append_range(next, it->second);
+ } else {
+ next.push_back(v);
+ }
+ }
+ if (next != current) {
+ // If at least one value was replaced, continue the lookup from there.
+ current = std::move(next);
+ continue;
+ }
- Value mappedValue = mapping.lookupOrNull(from);
- if (!mappedValue)
+ // Otherwise: Check if there is a mapping for the entire vector. Such
+ // mappings are materializations. (N:M mapping are not supported for value
+ // replacements.)
+ auto it = mapping.find(current);
+ if (it == mapping.end()) {
+ // No mapping found: The lookup stops here.
break;
- from = mappedValue;
+ }
----------------
zero9178 wrote:
Could we add a comment here that this is no functional requirement but part of an optimization to re-use existing materializations?
https://github.com/llvm/llvm-project/pull/116524
More information about the flang-commits
mailing list