diff --git a/lib/SILOptimizer/Utils/InstructionDeleter.cpp b/lib/SILOptimizer/Utils/InstructionDeleter.cpp index 643b0f9ea161f..ebaa13b6b9fdc 100644 --- a/lib/SILOptimizer/Utils/InstructionDeleter.cpp +++ b/lib/SILOptimizer/Utils/InstructionDeleter.cpp @@ -10,10 +10,11 @@ // //===----------------------------------------------------------------------===// +#include "swift/SILOptimizer/Utils/InstructionDeleter.h" #include "swift/SIL/SILFunction.h" +#include "swift/SIL/Test.h" #include "swift/SILOptimizer/Utils/ConstExpr.h" #include "swift/SILOptimizer/Utils/DebugOptUtils.h" -#include "swift/SILOptimizer/Utils/InstructionDeleter.h" #include "swift/SILOptimizer/Utils/InstOptUtils.h" using namespace swift; @@ -60,6 +61,21 @@ static bool isScopeAffectingInstructionDead(SILInstruction *inst, if (!hasOnlyEndOfScopeOrEndOfLifetimeUses(inst)) { return false; } + + // If inst has any owned move-only value as a result, deleting it may shorten + // that value's lifetime which is illegal according to language rules. + // + // In particular, this check is needed before returning true when + // getSingleValueCopyOrCast returns true. That function returns true for + // move_value instructions. And `move_value %moveOnlyValue` must not be + // deleted. + for (auto result : inst->getResults()) { + if (result->getType().isPureMoveOnly() && + result->getOwnershipKind() == OwnershipKind::Owned) { + return false; + } + } + // If inst is a copy or beginning of scope, inst is dead, since we know that // it is used only in a destroy_value or end-of-scope instruction. if (getSingleValueCopyOrCast(inst)) @@ -289,6 +305,22 @@ bool InstructionDeleter::deleteIfDead(SILInstruction *inst, bool fixLifetime) { return false; } +namespace swift::test { +// Arguments: +// - instruction: the instruction to delete +// Dumps: +// - the function +static FunctionTest DeleterDeleteIfDeadTest( + "deleter-delete-if-dead", [](auto &function, auto &arguments, auto &test) { + auto *inst = arguments.takeInstruction(); + InstructionDeleter deleter; + llvm::dbgs() << "Deleting-if-dead " << *inst; + auto deleted = deleter.deleteIfDead(inst); + llvm::dbgs() << "deleteIfDead returned " << deleted << "\n"; + function.dump(); + }); +} // namespace swift::test + void InstructionDeleter::forceDeleteAndFixLifetimes(SILInstruction *inst) { SILFunction *fun = inst->getFunction(); bool preserveDebugInfo = diff --git a/test/SILOptimizer/instruction_deleter.sil b/test/SILOptimizer/instruction_deleter.sil new file mode 100644 index 0000000000000..92015bc18e2cc --- /dev/null +++ b/test/SILOptimizer/instruction_deleter.sil @@ -0,0 +1,30 @@ +// RUN: %target-sil-opt -test-runner %s -o /dev/null 2>&1 | %FileCheck %s + +struct MOS : ~Copyable {} + +sil @getMOS : $() -> (@owned MOS) +sil @barrier : $() -> () + +// CHECK-LABEL: begin running test {{.*}} on dontDeleteDeadMoveOnlyValue +// CHECK: Deleting-if-dead {{.*}} move_value +// CHECK: deleteIfDead returned 0 +// CHECK-LABEL: sil [ossa] @dontDeleteDeadMoveOnlyValue : {{.*}} { +// CHECK: [[GET:%[^,]+]] = function_ref @getMOS +// CHECK: [[BARRIER:%[^,]+]] = function_ref @barrier +// CHECK: [[MOS:%[^,]+]] = apply [[GET]]() +// CHECK: [[MOV:%[^,]+]] = move_value [[MOS]] +// CHECK: apply [[BARRIER]]() +// CHECK: destroy_value [[MOV]] +// CHECK-LABEL: } // end sil function 'dontDeleteDeadMoveOnlyValue' +// CHECK-LABEL: end running test {{.*}} on dontDeleteDeadMoveOnlyValue +sil [ossa] @dontDeleteDeadMoveOnlyValue : $() -> () { + %get = function_ref @getMOS : $@convention(thin) () -> (@owned MOS) + %barrier = function_ref @barrier : $@convention(thin) () -> () + %mos = apply %get() : $@convention(thin) () -> (@owned MOS) + test_specification "deleter-delete-if-dead @instruction" + %mov = move_value %mos : $MOS + apply %barrier() : $@convention(thin) () -> () + destroy_value %mov : $MOS + %retval = tuple () + return %retval : $() +}