@@ -121,17 +121,8 @@ struct ConversionValueMapping {
121
121
// / false positives.
122
122
bool isMappedTo (Value value) const { return mappedTo.contains (value); }
123
123
124
- // / Lookup the most recently mapped values with the desired types in the
125
- // / mapping.
126
- // /
127
- // / Special cases:
128
- // / - If the desired type range is empty, simply return the most recently
129
- // / mapped values.
130
- // / - If there is no mapping to the desired types, also return the most
131
- // / recently mapped values.
132
- // / - If there is no mapping for the given values at all, return the given
133
- // / value.
134
- ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {}) const ;
124
+ // / Lookup a value in the mapping.
125
+ ValueVector lookup (const ValueVector &from) const ;
135
126
136
127
template <typename T>
137
128
struct IsValueVector : std::is_same<std::decay_t <T>, ValueVector> {};
@@ -185,54 +176,31 @@ struct ConversionValueMapping {
185
176
};
186
177
} // namespace
187
178
188
- ValueVector
189
- ConversionValueMapping::lookupOrDefault (Value from,
190
- TypeRange desiredTypes) const {
191
- // Try to find the deepest values that have the desired types. If there is no
192
- // such mapping, simply return the deepest values.
193
- ValueVector desiredValue;
194
- ValueVector current{from};
195
- do {
196
- // Store the current value if the types match.
197
- if (TypeRange (ValueRange (current)) == desiredTypes)
198
- desiredValue = current;
199
-
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 ()) {
226
- // No mapping found: The lookup stops here.
227
- break ;
228
- }
229
- current = it->second ;
230
- } while (true );
179
+ // / Marker attribute for pure type conversions. I.e., mappings whose only
180
+ // / purpose is to resolve a type mismatch. (In contrast, mappings that point to
181
+ // / the replacement values of a "replaceOp" call, etc., are not pure type
182
+ // / conversions.)
183
+ static const StringRef kPureTypeConversionMarker = " __pure_type_conversion__" ;
184
+
185
+ // / A vector of values is a pure type conversion if all values are defined by
186
+ // / the same operation and the operation has the `kPureTypeConversionMarker`
187
+ // / attribute.
188
+ static bool isPureTypeConversion (const ValueVector &values) {
189
+ assert (!values.empty () && " expected non-empty value vector" );
190
+ Operation *op = values.front ().getDefiningOp ();
191
+ for (Value v : llvm::drop_begin (values))
192
+ if (v.getDefiningOp () != op)
193
+ return false ;
194
+ return op && op->hasAttr (kPureTypeConversionMarker );
195
+ }
231
196
232
- // If the desired values were found use them, otherwise default to the leaf
233
- // values.
234
- // Note: If `desiredTypes` is empty, this function always returns `current`.
235
- return !desiredValue.empty () ? std::move (desiredValue) : std::move (current);
197
+ ValueVector ConversionValueMapping::lookup (const ValueVector &from) const {
198
+ auto it = mapping.find (from);
199
+ if (it == mapping.end ()) {
200
+ // No mapping found: The lookup stops here.
201
+ return {};
202
+ }
203
+ return it->second ;
236
204
}
237
205
238
206
// ===----------------------------------------------------------------------===//
@@ -930,7 +898,11 @@ struct ConversionPatternRewriterImpl : public RewriterBase::Listener {
930
898
// / recently mapped values.
931
899
// / - If there is no mapping for the given values at all, return the given
932
900
// / value.
933
- ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {}) const ;
901
+ // /
902
+ // / If `skipPureTypeConversions` is "true", materializations that are pure
903
+ // / type conversions are not considered.
904
+ ValueVector lookupOrDefault (Value from, TypeRange desiredTypes = {},
905
+ bool skipPureTypeConversions = false ) const ;
934
906
935
907
// / Lookup the given value within the map, or return an empty vector if the
936
908
// / value is not mapped. If it is mapped, this follows the same behavior
@@ -993,11 +965,19 @@ struct ConversionPatternRewriterImpl : public RewriterBase::Listener {
993
965
// / If `valuesToMap` is set to a non-null Value, then that value is mapped to
994
966
// / the results of the unresolved materialization in the conversion value
995
967
// / mapping.
968
+ // /
969
+ // / If `isPureTypeConversion` is "true", the materialization is created only
970
+ // / to resolve a type mismatch. That means it is not a regular value
971
+ // / replacement issued by the user. (Replacement values that are created
972
+ // / "out of thin air" appear like unresolved materializations because they are
973
+ // / unrealized_conversion_cast ops. However, they must be treated like
974
+ // / regular value replacements.)
996
975
ValueRange buildUnresolvedMaterialization (
997
976
MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc,
998
977
ValueVector valuesToMap, ValueRange inputs, TypeRange outputTypes,
999
978
Type originalType, const TypeConverter *converter,
1000
- UnrealizedConversionCastOp *castOp = nullptr );
979
+ UnrealizedConversionCastOp *castOp = nullptr ,
980
+ bool isPureTypeConversion = true );
1001
981
1002
982
// / Find a replacement value for the given SSA value in the conversion value
1003
983
// / mapping. The replacement value must have the same type as the given SSA
@@ -1264,10 +1244,77 @@ void ConversionPatternRewriterImpl::applyRewrites() {
1264
1244
// State Management
1265
1245
// ===----------------------------------------------------------------------===//
1266
1246
1267
- ValueVector
1268
- ConversionPatternRewriterImpl::lookupOrDefault (Value from,
1269
- TypeRange desiredTypes) const {
1270
- return mapping.lookupOrDefault (from, desiredTypes);
1247
+ ValueVector ConversionPatternRewriterImpl::lookupOrDefault (
1248
+ Value from, TypeRange desiredTypes, bool skipPureTypeConversions) const {
1249
+ // Helper function that looks up each value in `values` individually and then
1250
+ // composes the results. If that fails, it tries to look up the entire vector
1251
+ // at once.
1252
+ auto composedLookup = [&](const ValueVector &values) -> ValueVector {
1253
+ // If possible, replace each value with (one or multiple) mapped values.
1254
+ ValueVector next;
1255
+ for (Value v : values) {
1256
+ ValueVector r = mapping.lookup ({v});
1257
+ if (!r.empty ()) {
1258
+ llvm::append_range (next, r);
1259
+ } else {
1260
+ next.push_back (v);
1261
+ }
1262
+ }
1263
+ if (next != values) {
1264
+ // At least one value was replaced.
1265
+ return next;
1266
+ }
1267
+
1268
+ // Otherwise: Check if there is a mapping for the entire vector. Such
1269
+ // mappings are materializations. (N:M mapping are not supported for value
1270
+ // replacements.)
1271
+ //
1272
+ // Note: From a correctness point of view, materializations do not have to
1273
+ // be stored (and looked up) in the mapping. But for performance reasons,
1274
+ // we choose to reuse existing IR (when possible) instead of creating it
1275
+ // multiple times.
1276
+ ValueVector r = mapping.lookup (values);
1277
+ if (r.empty ()) {
1278
+ // No mapping found: The lookup stops here.
1279
+ return {};
1280
+ }
1281
+ return r;
1282
+ };
1283
+
1284
+ // Try to find the deepest values that have the desired types. If there is no
1285
+ // such mapping, simply return the deepest values.
1286
+ ValueVector desiredValue;
1287
+ ValueVector current{from};
1288
+ ValueVector lastNonMaterialization{from};
1289
+ do {
1290
+ // Store the current value if the types match.
1291
+ bool match = TypeRange (ValueRange (current)) == desiredTypes;
1292
+ if (skipPureTypeConversions) {
1293
+ // Skip pure type conversions, if requested.
1294
+ bool pureConversion = isPureTypeConversion (current);
1295
+ match &= !pureConversion;
1296
+ // Keep track of the last mapped value that was not a pure type
1297
+ // conversion.
1298
+ if (!pureConversion)
1299
+ lastNonMaterialization = current;
1300
+ }
1301
+ if (match)
1302
+ desiredValue = current;
1303
+
1304
+ // Lookup next value in the mapping.
1305
+ ValueVector next = composedLookup (current);
1306
+ if (next.empty ())
1307
+ break ;
1308
+ current = std::move (next);
1309
+ } while (true );
1310
+
1311
+ // If the desired values were found use them, otherwise default to the leaf
1312
+ // values. (Skip pure type conversions, if requested.)
1313
+ if (!desiredTypes.empty ())
1314
+ return desiredValue;
1315
+ if (skipPureTypeConversions)
1316
+ return lastNonMaterialization;
1317
+ return current;
1271
1318
}
1272
1319
1273
1320
ValueVector
@@ -1324,10 +1371,13 @@ LogicalResult ConversionPatternRewriterImpl::remapValues(
1324
1371
Location operandLoc = inputLoc ? *inputLoc : operand.getLoc ();
1325
1372
1326
1373
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));
1374
+ // The current pattern does not have a type converter. Pass the most
1375
+ // recently mapped values, excluding materializations. Materializations
1376
+ // are intentionally excluded because their presence may depend on other
1377
+ // patterns. Including materializations would make the lookup fragile
1378
+ // and unpredictable.
1379
+ remapped.push_back (lookupOrDefault (operand, /* desiredTypes=*/ {},
1380
+ /* skipPureTypeConversions=*/ true ));
1331
1381
continue ;
1332
1382
}
1333
1383
@@ -1356,7 +1406,8 @@ LogicalResult ConversionPatternRewriterImpl::remapValues(
1356
1406
}
1357
1407
1358
1408
// Create a materialization for the most recently mapped values.
1359
- repl = lookupOrDefault (operand);
1409
+ repl = lookupOrDefault (operand, /* desiredTypes=*/ {},
1410
+ /* skipPureTypeConversions=*/ true );
1360
1411
ValueRange castValues = buildUnresolvedMaterialization (
1361
1412
MaterializationKind::Target, computeInsertPoint (repl), operandLoc,
1362
1413
/* valuesToMap=*/ repl, /* inputs=*/ repl, /* outputTypes=*/ legalTypes,
@@ -1482,7 +1533,8 @@ Block *ConversionPatternRewriterImpl::applySignatureConversion(
1482
1533
OpBuilder::InsertPoint (newBlock, newBlock->begin ()),
1483
1534
origArg.getLoc (),
1484
1535
/* valuesToMap=*/ {}, /* inputs=*/ ValueRange (),
1485
- /* outputTypes=*/ origArgType, /* originalType=*/ Type (), converter)
1536
+ /* outputTypes=*/ origArgType, /* originalType=*/ Type (), converter,
1537
+ /* castOp=*/ nullptr , /* isPureTypeConversion=*/ false )
1486
1538
.front ();
1487
1539
replaceUsesOfBlockArgument (origArg, mat, converter);
1488
1540
continue ;
@@ -1523,7 +1575,7 @@ ValueRange ConversionPatternRewriterImpl::buildUnresolvedMaterialization(
1523
1575
MaterializationKind kind, OpBuilder::InsertPoint ip, Location loc,
1524
1576
ValueVector valuesToMap, ValueRange inputs, TypeRange outputTypes,
1525
1577
Type originalType, const TypeConverter *converter,
1526
- UnrealizedConversionCastOp *castOp) {
1578
+ UnrealizedConversionCastOp *castOp, bool isPureTypeConversion ) {
1527
1579
assert ((!originalType || kind == MaterializationKind::Target) &&
1528
1580
" original type is valid only for target materializations" );
1529
1581
assert (TypeRange (inputs) != outputTypes &&
@@ -1535,6 +1587,8 @@ ValueRange ConversionPatternRewriterImpl::buildUnresolvedMaterialization(
1535
1587
builder.setInsertionPoint (ip.getBlock (), ip.getPoint ());
1536
1588
auto convertOp =
1537
1589
UnrealizedConversionCastOp::create (builder, loc, outputTypes, inputs);
1590
+ if (isPureTypeConversion)
1591
+ convertOp->setAttr (kPureTypeConversionMarker , builder.getUnitAttr ());
1538
1592
if (!valuesToMap.empty ())
1539
1593
mapping.map (valuesToMap, convertOp.getResults ());
1540
1594
if (castOp)
@@ -1650,7 +1704,8 @@ void ConversionPatternRewriterImpl::replaceOp(
1650
1704
MaterializationKind::Source, computeInsertPoint (result),
1651
1705
result.getLoc (), /* valuesToMap=*/ {result}, /* inputs=*/ ValueRange (),
1652
1706
/* outputTypes=*/ result.getType (), /* originalType=*/ Type (),
1653
- currentTypeConverter);
1707
+ currentTypeConverter, /* castOp=*/ nullptr ,
1708
+ /* isPureTypeConversion=*/ false );
1654
1709
continue ;
1655
1710
}
1656
1711
@@ -2902,6 +2957,10 @@ LogicalResult OperationConverter::convertOperations(ArrayRef<Operation *> ops) {
2902
2957
SmallVector<UnrealizedConversionCastOp> remainingCastOps;
2903
2958
reconcileUnrealizedCasts (allCastOps, &remainingCastOps);
2904
2959
2960
+ // Drop markers.
2961
+ for (UnrealizedConversionCastOp castOp : remainingCastOps)
2962
+ castOp->removeAttr (kPureTypeConversionMarker );
2963
+
2905
2964
// Try to legalize all unresolved materializations.
2906
2965
if (config.buildMaterializations ) {
2907
2966
IRRewriter rewriter (rewriterImpl.context , config.listener );
0 commit comments