@@ -1182,51 +1182,103 @@ bool swift::canUseScalarCheckedCastInstructions(SILModule &M,
1182
1182
// / using a scalar cast operation.
1183
1183
void swift::emitIndirectConditionalCastWithScalar (
1184
1184
SILBuilder &B, ModuleDecl *M, SILLocation loc,
1185
- CastConsumptionKind consumption, SILValue src , CanType sourceType,
1186
- SILValue dest , CanType targetType, SILBasicBlock *indirectSuccBB,
1185
+ CastConsumptionKind consumption, SILValue srcAddr , CanType sourceType,
1186
+ SILValue destAddr , CanType targetType, SILBasicBlock *indirectSuccBB,
1187
1187
SILBasicBlock *indirectFailBB, ProfileCounter TrueCount,
1188
1188
ProfileCounter FalseCount) {
1189
1189
assert (canUseScalarCheckedCastInstructions (B.getModule (),
1190
1190
sourceType, targetType));
1191
1191
1192
- // We only need a different failure block if the cast consumption
1193
- // requires us to destroy the source value.
1194
- SILBasicBlock *scalarFailBB;
1195
- if (!shouldDestroyOnFailure (consumption)) {
1196
- scalarFailBB = indirectFailBB;
1197
- } else {
1198
- scalarFailBB = B.splitBlockForFallthrough ();
1199
- }
1200
-
1201
- // We always need a different success block.
1192
+ // Create our successor and fail blocks.
1193
+ SILBasicBlock *scalarFailBB = B.splitBlockForFallthrough ();
1202
1194
SILBasicBlock *scalarSuccBB = B.splitBlockForFallthrough ();
1203
1195
1204
- auto &srcTL = B.getModule ().Types .getTypeLowering (src->getType ());
1205
-
1206
- // Always take; this works under an assumption that retaining the
1207
- // result is equivalent to retaining the source. That means that
1208
- // these casts would not be appropriate for bridging-like conversions.
1209
- SILValue srcValue = srcTL.emitLoadOfCopy (B, loc, src, IsTake);
1196
+ // Always take; this works under an assumption that retaining the result is
1197
+ // equivalent to retaining the source. That means that these casts would not
1198
+ // be appropriate for bridging-like conversions.
1199
+ //
1200
+ // Our plan is:
1201
+ //
1202
+ // 1. If the original cast was a take_always cast, then we take from our
1203
+ // memory location in the caller, store the value into dest in the success
1204
+ // block, and perform a destroy of our default argument in the failure block.
1205
+ //
1206
+ // 2. If the original cast was copy_on_success, then with ownership we borrow,
1207
+ // copy in the success path and store back into the source slot after copying.
1208
+ //
1209
+ // 3. If the original cast was take_on_success, then on success we place the
1210
+ // casted value into dest and on failure, store the original value back into
1211
+ // src.
1212
+ SILType targetValueType = destAddr->getType ().getObjectType ();
1213
+ // Inline constructor
1214
+ auto srcValue = ([&]() -> SILValue {
1215
+ if (consumption == CastConsumptionKind::CopyOnSuccess)
1216
+ return B.emitLoadBorrowOperation (loc, srcAddr);
1217
+ return B.emitLoadValueOperation (loc, srcAddr, LoadOwnershipQualifier::Take);
1218
+ })();
1210
1219
1211
- SILType targetValueType = dest->getType ().getObjectType ();
1212
1220
B.createCheckedCastBranch (loc, /* exact*/ false , srcValue, targetValueType,
1213
1221
scalarSuccBB, scalarFailBB, TrueCount, FalseCount);
1214
1222
1215
1223
// Emit the success block.
1216
1224
B.setInsertionPoint (scalarSuccBB); {
1217
- auto &targetTL = B.getModule ().Types .getTypeLowering (targetValueType);
1218
1225
SILValue succValue = scalarSuccBB->createPhiArgument (
1219
- targetValueType, ValueOwnershipKind::Owned);
1220
- if (!shouldTakeOnSuccess (consumption))
1221
- targetTL.emitCopyValue (B, loc, succValue);
1222
- targetTL.emitStoreOfCopy (B, loc, succValue, dest, IsInitialization);
1226
+ targetValueType, srcValue.getOwnershipKind ());
1227
+
1228
+ switch (consumption) {
1229
+ // On success, we take with both take_always and take_on_success.
1230
+ case CastConsumptionKind::TakeAlways:
1231
+ case CastConsumptionKind::TakeOnSuccess:
1232
+ break ;
1233
+ case CastConsumptionKind::CopyOnSuccess: {
1234
+ SILValue originalSuccValue = succValue;
1235
+ succValue = B.emitCopyValueOperation (loc, succValue);
1236
+ B.emitEndBorrowOperation (loc, originalSuccValue);
1237
+ B.emitEndBorrowOperation (loc, srcValue);
1238
+ break ;
1239
+ }
1240
+ case CastConsumptionKind::BorrowAlways:
1241
+ llvm_unreachable (" should never see a borrow_always here" );
1242
+ }
1243
+
1244
+ // And then store the succValue into dest.
1245
+ B.emitStoreValueOperation (loc, succValue, destAddr,
1246
+ StoreOwnershipQualifier::Init);
1223
1247
B.createBranch (loc, indirectSuccBB);
1224
1248
}
1225
1249
1226
1250
// Emit the failure block.
1227
- if (shouldDestroyOnFailure (consumption)) {
1228
- B.setInsertionPoint (scalarFailBB);
1229
- srcTL.emitDestroyValue (B, loc, srcValue);
1251
+ B.setInsertionPoint (scalarFailBB);
1252
+ {
1253
+ SILValue failValue = srcValue;
1254
+
1255
+ // If we have ownership, we need to create something for the default
1256
+ // argument. Otherwise, we just use the input argument to the
1257
+ // checked_cast_br.
1258
+ if (B.hasOwnership ()) {
1259
+ failValue = scalarFailBB->createPhiArgument (srcValue->getType (),
1260
+ srcValue.getOwnershipKind ());
1261
+ }
1262
+
1263
+ switch (consumption) {
1264
+ case CastConsumptionKind::TakeAlways:
1265
+ // We need to destroy the fail value if we have take_always.
1266
+ B.emitDestroyValueOperation (loc, failValue);
1267
+ break ;
1268
+ case CastConsumptionKind::TakeOnSuccess:
1269
+ // If we have take_on_success, since we failed, just store the value back
1270
+ // into the src location that we originally took from.
1271
+ B.emitStoreValueOperation (loc, failValue, srcAddr,
1272
+ StoreOwnershipQualifier::Init);
1273
+ break ;
1274
+ case CastConsumptionKind::CopyOnSuccess:
1275
+ B.emitEndBorrowOperation (loc, failValue);
1276
+ B.emitEndBorrowOperation (loc, srcValue);
1277
+ break ;
1278
+ case CastConsumptionKind::BorrowAlways:
1279
+ llvm_unreachable (" borrow_on_success should never appear here" );
1280
+ }
1281
+
1230
1282
B.createBranch (loc, indirectFailBB);
1231
1283
}
1232
1284
}
0 commit comments