@@ -131,16 +131,25 @@ struct ConversionValueMapping {
131
131
// / recently mapped values.
132
132
// / - If there is no mapping for the given values at all, return the given
133
133
// / value.
134
- ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {}) const ;
134
+ // /
135
+ // / If `skipMaterializations` is true, materializations are not considered.
136
+ ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {},
137
+ bool skipMaterializations = false ) const ;
138
+
139
+ // / Lookup a value from the mapping. (Just once, not following the chain of
140
+ // / potential mappings.) Look for actual replacements first, then for
141
+ // / materializations. The materializations lookup can be skipped.
142
+ ValueVector lookupSingleStep (const ValueVector &from,
143
+ bool skipMaterializations = false ) const ;
135
144
136
145
template <typename T>
137
146
struct IsValueVector : std::is_same<std::decay_t <T>, ValueVector> {};
138
147
139
148
// / Map a value vector to the one provided.
140
149
template <typename OldVal, typename NewVal>
141
150
std::enable_if_t <IsValueVector<OldVal>::value && IsValueVector<NewVal>::value>
142
- map (OldVal &&oldVal, NewVal &&newVal) {
143
- LLVM_DEBUG ( {
151
+ map (OldVal &&oldVal, NewVal &&newVal, bool isOnlyTypeConversion = false ) {
152
+ auto checkCircularMapping = [&]( auto &mapping) {
144
153
ValueVector next (newVal);
145
154
while (true ) {
146
155
assert (next != oldVal && " inserting cyclic mapping" );
@@ -149,45 +158,117 @@ struct ConversionValueMapping {
149
158
break ;
150
159
next = it->second ;
151
160
}
152
- });
161
+ };
162
+ (void )checkCircularMapping;
163
+
153
164
mappedTo.insert_range (newVal);
154
165
155
- mapping[std::forward<OldVal>(oldVal)] = std::forward<NewVal>(newVal);
166
+ if (isOnlyTypeConversion) {
167
+ // This is a materialization.
168
+ LLVM_DEBUG ({ checkCircularMapping (materializations); });
169
+ materializations[std::forward<OldVal>(oldVal)] =
170
+ std::forward<NewVal>(newVal);
171
+ } else {
172
+ // This is a regular value replacement.
173
+ LLVM_DEBUG ({ checkCircularMapping (mapping); });
174
+ mapping[std::forward<OldVal>(oldVal)] = std::forward<NewVal>(newVal);
175
+ }
156
176
}
157
177
158
178
// / Map a value vector or single value to the one provided.
159
179
template <typename OldVal, typename NewVal>
160
180
std::enable_if_t <!IsValueVector<OldVal>::value ||
161
181
!IsValueVector<NewVal>::value>
162
- map (OldVal &&oldVal, NewVal &&newVal) {
182
+ map (OldVal &&oldVal, NewVal &&newVal, bool isOnlyTypeConversion = false ) {
163
183
if constexpr (IsValueVector<OldVal>{}) {
164
- map (std::forward<OldVal>(oldVal), ValueVector{newVal});
184
+ map (std::forward<OldVal>(oldVal), ValueVector{newVal},
185
+ isOnlyTypeConversion);
165
186
} else if constexpr (IsValueVector<NewVal>{}) {
166
- map (ValueVector{oldVal}, std::forward<NewVal>(newVal));
187
+ map (ValueVector{oldVal}, std::forward<NewVal>(newVal),
188
+ isOnlyTypeConversion);
167
189
} else {
168
- map (ValueVector{oldVal}, ValueVector{newVal});
190
+ map (ValueVector{oldVal}, ValueVector{newVal}, isOnlyTypeConversion );
169
191
}
170
192
}
171
193
172
- void map (Value oldVal, SmallVector<Value> &&newVal) {
194
+ void map (Value oldVal, SmallVector<Value> &&newVal,
195
+ bool isOnlyTypeConversion = false ) {
173
196
map (ValueVector{oldVal}, ValueVector (std::move (newVal)));
174
197
}
175
198
176
199
// / Drop the last mapping for the given values.
177
- void erase (const ValueVector &value) { mapping.erase (value); }
200
+ void erase (const ValueVector &value) {
201
+ mapping.erase (value);
202
+ materializations.erase (value);
203
+ }
178
204
179
205
private:
180
- // / Current value mappings .
206
+ // / Mapping of actual replacements .
181
207
DenseMap<ValueVector, ValueVector, ValueVectorMapInfo> mapping;
182
208
209
+ // / Mapping of materializations that are created only to resolve type
210
+ // / mismatches.
211
+ DenseMap<ValueVector, ValueVector, ValueVectorMapInfo> materializations;
212
+
183
213
// / All SSA values that are mapped to. May contain false positives.
184
214
DenseSet<Value> mappedTo;
185
215
};
186
216
} // namespace
187
217
188
218
ValueVector
189
- ConversionValueMapping::lookupOrDefault (Value from,
190
- TypeRange desiredTypes) const {
219
+ ConversionValueMapping::lookupSingleStep (const ValueVector &from,
220
+ bool skipMaterializations) const {
221
+ // Continue the lookup on each value separately. (Each value could have been
222
+ // mapped to one or multiple other values.)
223
+ ValueVector next;
224
+ for (Value v : from) {
225
+ // First check regular value replacements.
226
+ auto it = mapping.find ({v});
227
+ if (it != mapping.end ()) {
228
+ llvm::append_range (next, it->second );
229
+ continue ;
230
+ }
231
+ if (skipMaterializations) {
232
+ next.push_back (v);
233
+ continue ;
234
+ }
235
+ // Then check materializations.
236
+ it = materializations.find ({v});
237
+ if (it != materializations.end ()) {
238
+ llvm::append_range (next, it->second );
239
+ continue ;
240
+ }
241
+ next.push_back (v);
242
+ }
243
+
244
+ if (next != from)
245
+ return next;
246
+
247
+ // Otherwise: Check if there is a mapping for the entire vector. Such
248
+ // mappings are materializations. (N:M mapping are not supported for value
249
+ // replacements.)
250
+ //
251
+ // Note: From a correctness point of view, materializations do not have to
252
+ // be stored (and looked up) in the mapping. But for performance reasons,
253
+ // we choose to reuse existing IR (when possible) instead of creating it
254
+ // multiple times.
255
+ //
256
+ // First check regular value replacements.
257
+ auto it = mapping.find (from);
258
+ if (it != mapping.end ())
259
+ return it->second ;
260
+ if (skipMaterializations)
261
+ return {};
262
+ // Then check materializations.
263
+ it = materializations.find (from);
264
+ if (it != materializations.end ())
265
+ return it->second ;
266
+ return {};
267
+ }
268
+
269
+ ValueVector
270
+ ConversionValueMapping::lookupOrDefault (Value from, TypeRange desiredTypes,
271
+ bool skipMaterializations) const {
191
272
// Try to find the deepest values that have the desired types. If there is no
192
273
// such mapping, simply return the deepest values.
193
274
ValueVector desiredValue;
@@ -197,36 +278,13 @@ ConversionValueMapping::lookupOrDefault(Value from,
197
278
if (TypeRange (ValueRange (current)) == desiredTypes)
198
279
desiredValue = current;
199
280
200
- // If possible, Replace each value with (one or multiple) mapped values.
201
- ValueVector next;
202
- for (Value v : current) {
203
- auto it = mapping.find ({v});
204
- if (it != mapping.end ()) {
205
- llvm::append_range (next, it->second );
206
- } else {
207
- next.push_back (v);
208
- }
209
- }
210
- if (next != current) {
211
- // If at least one value was replaced, continue the lookup from there.
212
- current = std::move (next);
213
- continue ;
214
- }
215
-
216
- // Otherwise: Check if there is a mapping for the entire vector. Such
217
- // mappings are materializations. (N:M mapping are not supported for value
218
- // replacements.)
219
- //
220
- // Note: From a correctness point of view, materializations do not have to
221
- // be stored (and looked up) in the mapping. But for performance reasons,
222
- // we choose to reuse existing IR (when possible) instead of creating it
223
- // multiple times.
224
- auto it = mapping.find (current);
225
- if (it == mapping.end ()) {
281
+ ValueVector next = lookupSingleStep (current, skipMaterializations);
282
+ if (next.empty ()) {
226
283
// No mapping found: The lookup stops here.
227
284
break ;
228
285
}
229
- current = it->second ;
286
+
287
+ current = std::move (next);
230
288
} while (true );
231
289
232
290
// If the desired values were found use them, otherwise default to the leaf
@@ -930,7 +988,10 @@ struct ConversionPatternRewriterImpl : public RewriterBase::Listener {
930
988
// / recently mapped values.
931
989
// / - If there is no mapping for the given values at all, return the given
932
990
// / value.
933
- ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {}) const ;
991
+ // /
992
+ // / If `skipMaterializations` is true, materializations are not considered.
993
+ ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {},
994
+ bool skipMaterializations = false ) const ;
934
995
935
996
// / Lookup the given value within the map, or return an empty vector if the
936
997
// / value is not mapped. If it is mapped, this follows the same behavior
@@ -993,11 +1054,18 @@ struct ConversionPatternRewriterImpl : public RewriterBase::Listener {
993
1054
// / If `valuesToMap` is set to a non-null Value, then that value is mapped to
994
1055
// / the results of the unresolved materialization in the conversion value
995
1056
// / mapping.
1057
+ // /
1058
+ // / If `isOnlyTypeConversion` is "true", the materialization is created to
1059
+ // / resolve a type mismatch, and not a regular value replacement issued by
1060
+ // / the user. (Replacement values that are created "out of thin air" are
1061
+ // / treated appear like unresolved materializations, but are not just type
1062
+ // / conversions.)
996
1063
ValueRange buildUnresolvedMaterialization (
997
1064
MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc,
998
1065
ValueVector valuesToMap, ValueRange inputs, TypeRange outputTypes,
999
1066
Type originalType, const TypeConverter *converter,
1000
- UnrealizedConversionCastOp *castOp = nullptr );
1067
+ UnrealizedConversionCastOp *castOp = nullptr ,
1068
+ bool isOnlyTypeConversion = true );
1001
1069
1002
1070
// / Find a replacement value for the given SSA value in the conversion value
1003
1071
// / mapping. The replacement value must have the same type as the given SSA
@@ -1264,10 +1332,9 @@ void ConversionPatternRewriterImpl::applyRewrites() {
1264
1332
// State Management
1265
1333
// ===----------------------------------------------------------------------===//
1266
1334
1267
- ValueVector
1268
- ConversionPatternRewriterImpl::lookupOrDefault (Value from,
1269
- TypeRange desiredTypes) const {
1270
- return mapping.lookupOrDefault (from, desiredTypes);
1335
+ ValueVector ConversionPatternRewriterImpl::lookupOrDefault (
1336
+ Value from, TypeRange desiredTypes, bool skipMaterializations) const {
1337
+ return mapping.lookupOrDefault (from, desiredTypes, skipMaterializations);
1271
1338
}
1272
1339
1273
1340
ValueVector
@@ -1324,10 +1391,13 @@ LogicalResult ConversionPatternRewriterImpl::remapValues(
1324
1391
Location operandLoc = inputLoc ? *inputLoc : operand.getLoc ();
1325
1392
1326
1393
if (!currentTypeConverter) {
1327
- // The current pattern does not have a type converter. I.e., it does not
1328
- // distinguish between legal and illegal types. For each operand, simply
1329
- // pass through the most recently mapped values.
1330
- remapped.push_back (lookupOrDefault (operand));
1394
+ // The current pattern does not have a type converter. Pass the most
1395
+ // recently mapped values, excluding materializations. Materializations
1396
+ // are intentionally excluded because their presence may depend on other
1397
+ // patterns. Including materializations would make the lookup fragile
1398
+ // and unpredictable.
1399
+ remapped.push_back (lookupOrDefault (operand, /* desiredTypes=*/ {},
1400
+ /* skipMaterializations=*/ true ));
1331
1401
continue ;
1332
1402
}
1333
1403
@@ -1356,7 +1426,8 @@ LogicalResult ConversionPatternRewriterImpl::remapValues(
1356
1426
}
1357
1427
1358
1428
// Create a materialization for the most recently mapped values.
1359
- repl = lookupOrDefault (operand);
1429
+ repl = lookupOrDefault (operand, /* desiredTypes=*/ {},
1430
+ /* skipMaterializations=*/ true );
1360
1431
ValueRange castValues = buildUnresolvedMaterialization (
1361
1432
MaterializationKind::Target, computeInsertPoint (repl), operandLoc,
1362
1433
/* valuesToMap=*/ repl, /* inputs=*/ repl, /* outputTypes=*/ legalTypes,
@@ -1482,7 +1553,8 @@ Block *ConversionPatternRewriterImpl::applySignatureConversion(
1482
1553
OpBuilder::InsertPoint (newBlock, newBlock->begin ()),
1483
1554
origArg.getLoc (),
1484
1555
/* valuesToMap=*/ {}, /* inputs=*/ ValueRange (),
1485
- /* outputTypes=*/ origArgType, /* originalType=*/ Type (), converter)
1556
+ /* outputTypes=*/ origArgType, /* originalType=*/ Type (), converter,
1557
+ /* castOp=*/ nullptr , /* isOnlyTypeConversion=*/ false )
1486
1558
.front ();
1487
1559
replaceUsesOfBlockArgument (origArg, mat, converter);
1488
1560
continue ;
@@ -1523,7 +1595,7 @@ ValueRange ConversionPatternRewriterImpl::buildUnresolvedMaterialization(
1523
1595
MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc,
1524
1596
ValueVector valuesToMap, ValueRange inputs, TypeRange outputTypes,
1525
1597
Type originalType, const TypeConverter *converter,
1526
- UnrealizedConversionCastOp *castOp) {
1598
+ UnrealizedConversionCastOp *castOp, bool isOnlyTypeConversion ) {
1527
1599
assert ((!originalType || kind == MaterializationKind::Target) &&
1528
1600
" original type is valid only for target materializations" );
1529
1601
assert (TypeRange (inputs) != outputTypes &&
@@ -1536,7 +1608,7 @@ ValueRange ConversionPatternRewriterImpl::buildUnresolvedMaterialization(
1536
1608
auto convertOp =
1537
1609
UnrealizedConversionCastOp::create (builder, loc, outputTypes, inputs);
1538
1610
if (!valuesToMap.empty ())
1539
- mapping.map (valuesToMap, convertOp.getResults ());
1611
+ mapping.map (valuesToMap, convertOp.getResults (), isOnlyTypeConversion );
1540
1612
if (castOp)
1541
1613
*castOp = convertOp;
1542
1614
unresolvedMaterializations[convertOp] =
@@ -1650,7 +1722,8 @@ void ConversionPatternRewriterImpl::replaceOp(
1650
1722
MaterializationKind::Source, computeInsertPoint (result),
1651
1723
result.getLoc (), /* valuesToMap=*/ {result}, /* inputs=*/ ValueRange (),
1652
1724
/* outputTypes=*/ result.getType (), /* originalType=*/ Type (),
1653
- currentTypeConverter);
1725
+ currentTypeConverter, /* castOp=*/ nullptr ,
1726
+ /* isOnlyTypeConversion=*/ false );
1654
1727
continue ;
1655
1728
}
1656
1729
0 commit comments