@@ -189,21 +189,53 @@ static bool hasOnlyEndOfScopeOrDestroyUses(SILInstruction *inst) {
189
189
return true ;
190
190
}
191
191
192
- // / Return true iff the \p applySite calls a constant evaluable function and if
193
- // / it is read-only which implies the following:
194
- // / (1) The call does not write into any memory location.
192
+ unsigned swift::getNumInOutArguments (FullApplySite applySite) {
193
+ assert (applySite);
194
+ auto substConv = applySite.getSubstCalleeConv ();
195
+ unsigned numIndirectResults = substConv.getNumIndirectSILResults ();
196
+ unsigned numInOutArguments = 0 ;
197
+ for (unsigned argIndex = 0 ; argIndex < applySite.getNumArguments ();
198
+ argIndex++) {
199
+ // Skip indirect results.
200
+ if (argIndex < numIndirectResults) {
201
+ continue ;
202
+ }
203
+ auto paramNumber = argIndex - numIndirectResults;
204
+ auto ParamConvention =
205
+ substConv.getParameters ()[paramNumber].getConvention ();
206
+ switch (ParamConvention) {
207
+ case ParameterConvention::Indirect_Inout:
208
+ case ParameterConvention::Indirect_InoutAliasable: {
209
+ numInOutArguments++;
210
+ break ;
211
+ default :
212
+ break ;
213
+ }
214
+ }
215
+ }
216
+ return numInOutArguments;
217
+ }
218
+
219
+ // / Return true iff the \p applySite calls a constant-evaluable function and
220
+ // / it is non-generic and read/destroy only, which means that the call can do
221
+ // / only the following and nothing else:
222
+ // / (1) The call may read any memory location.
195
223
// / (2) The call may destroy owned parameters i.e., consume them.
196
- // / (3) The call does not throw or exit the program.
197
- static bool isReadOnlyConstantEvaluableCall (FullApplySite applySite) {
224
+ // / (3) The call may write into memory locations newly created by the call.
225
+ // / (4) The call may use assertions, which traps at runtime on failure.
226
+ // / (5) The call may return a non-generic value.
227
+ // / Essentially, these are calls whose "effect" is visible only in their return
228
+ // / value or through the parameters that are destroyed. The return value
229
+ // / is also guaranteed to have value semantics as it is non-generic and
230
+ // / reference semantics is not constant evaluable.
231
+ static bool isNonGenericReadOnlyConstantEvaluableCall (FullApplySite applySite) {
198
232
assert (applySite);
199
233
SILFunction *callee = applySite.getCalleeFunction ();
200
234
if (!callee || !isConstantEvaluable (callee)) {
201
235
return false ;
202
236
}
203
- // Here all effects of the call is restricted to its indirect results, which
204
- // must have value semantics. If there are no indirect results, the call must
205
- // be read-only, except for consuming its operands.
206
- return applySite.getNumIndirectSILResults () == 0 ;
237
+ return !applySite.hasSubstitutions () && !getNumInOutArguments (applySite) &&
238
+ !applySite.getNumIndirectSILResults ();
207
239
}
208
240
209
241
// / A scope-affecting instruction is an instruction which may end the scope of
@@ -270,23 +302,25 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst) {
270
302
return true ;
271
303
}
272
304
case SILInstructionKind::ApplyInst: {
273
- // The following property holds for constant evaluable functions:
305
+ // The following property holds for constant-evaluable functions that do
306
+ // not take arguments of generic type:
274
307
// 1. they do not create objects having deinitializers with global
308
+ // side effects, as they can only create objects consisting of trivial
309
+ // values, (non-generic) arrays and strings.
310
+ // 2. they do not use global variables or call arbitrary functions with
275
311
// side effects.
276
- // 2. they do not use global variables and will only use objects reachable
277
- // from parameters.
278
312
// The above two properties imply that a value returned by a constant
279
- // evaluable function either does not have a deinitializer with global side
280
- // effects, or if it does, the deinitializer that has the global side effect
281
- // must be that of a parameter.
313
+ // evaluable function does not have a deinitializer with global side
314
+ // effects. Therefore, the deinitializer can be sinked.
282
315
//
283
- // A read-only constant evaluable call only reads and/or destroys its
284
- // parameters. Therefore, if its return value is used only in destroys, the
285
- // constant evaluable call can be removed provided the parameters it
286
- // consumes are explicitly destroyed at the call site, which is taken care
287
- // of by the function: \c deleteInstruction
316
+ // A generic, read-only constant evaluable call only reads and/or
317
+ // destroys its (non-generic) parameters. It therefore cannot have any
318
+ // side effects (note that parameters being non-generic have value
319
+ // semantics). Therefore, the constant evaluable call can be removed
320
+ // provided the parameter lifetimes are handled correctly, which is taken
321
+ // care of by the function: \c deleteInstruction.
288
322
FullApplySite applySite (cast<ApplyInst>(inst));
289
- return isReadOnlyConstantEvaluableCall (applySite);
323
+ return isNonGenericReadOnlyConstantEvaluableCall (applySite);
290
324
}
291
325
default : {
292
326
return false ;
@@ -318,9 +352,14 @@ static void destroyConsumedOperandOfDeadInst(Operand &operand) {
318
352
SILValue operandValue = operand.get ();
319
353
if (operandValue->getType ().isTrivial (*fun))
320
354
return ;
355
+ // Ignore type-dependent operands which are not real operands but are just
356
+ // there to create use-def dependencies.
357
+ if (deadInst->isTypeDependentOperand (operand))
358
+ return ;
321
359
// A scope ending instruction cannot be deleted in isolation without removing
322
360
// the instruction defining its operand as well.
323
361
assert (!isEndOfScopeMarker (deadInst) && !isa<DestroyValueInst>(deadInst) &&
362
+ !isa<DestroyAddrInst>(deadInst) &&
324
363
" lifetime ending instruction is deleted without its operand" );
325
364
ValueOwnershipKind operandOwnershipKind = operandValue.getOwnershipKind ();
326
365
UseLifetimeConstraint lifetimeConstraint =
@@ -379,7 +418,10 @@ void InstructionDeleter::deleteInstruction(SILInstruction *inst,
379
418
// First drop all references from all instructions to be deleted and then
380
419
// erase the instruction. Note that this is done in this order so that when an
381
420
// instruction is deleted, its uses would have dropped their references.
421
+ // Note that the toDeleteInsts must also be removed from the tracked
422
+ // deadInstructions.
382
423
for (SILInstruction *inst : toDeleteInsts) {
424
+ deadInstructions.remove (inst);
383
425
inst->dropAllReferences ();
384
426
}
385
427
for (SILInstruction *inst : toDeleteInsts) {
@@ -428,6 +470,14 @@ static bool hasOnlyIncidentalUses(SILInstruction *inst,
428
470
return true ;
429
471
}
430
472
473
+ void InstructionDeleter::deleteIfDead (SILInstruction *inst,
474
+ CallbackTy callback) {
475
+ if (isInstructionTriviallyDead (inst) ||
476
+ isScopeAffectingInstructionDead (inst)) {
477
+ deleteInstruction (inst, callback, /* Fix lifetime of operands*/ true );
478
+ }
479
+ }
480
+
431
481
void InstructionDeleter::forceDeleteAndFixLifetimes (SILInstruction *inst,
432
482
CallbackTy callback) {
433
483
SILFunction *fun = inst->getFunction ();
@@ -447,6 +497,18 @@ void InstructionDeleter::forceDelete(SILInstruction *inst,
447
497
deleteInstruction (inst, callback, /* Fix lifetime of operands*/ false );
448
498
}
449
499
500
+ void InstructionDeleter::recursivelyDeleteUsersIfDead (SILInstruction *inst,
501
+ CallbackTy callback) {
502
+ SmallVector<SILInstruction *, 8 > users;
503
+ for (SILValue result : inst->getResults ())
504
+ for (Operand *use : result->getUses ())
505
+ users.push_back (use->getUser ());
506
+
507
+ for (SILInstruction *user : users)
508
+ recursivelyDeleteUsersIfDead (user, callback);
509
+ deleteIfDead (inst, callback);
510
+ }
511
+
450
512
void swift::eliminateDeadInstruction (SILInstruction *inst,
451
513
CallbackTy callback) {
452
514
InstructionDeleter deleter;
0 commit comments