diff --git a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp index 9190b6ddbd367..dfe563f2b7a59 100644 --- a/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp +++ b/lib/SILOptimizer/Transforms/SSADestroyHoisting.cpp @@ -870,6 +870,11 @@ bool hoistDestroys(SILValue root, bool ignoreDeinitBarriers, BasicCalleeAnalysis *calleeAnalysis) { LLVM_DEBUG(llvm::dbgs() << "Performing destroy hoisting on " << root); + // Don't canonicalize the lifetimes of addresses of move-only type. + // According to language rules, they are fixed. + if (root->getType().isMoveOnly()) + return false; + SILFunction *function = root->getFunction(); if (!function) return false; diff --git a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp index d3f1965847de7..771459ee6f66a 100644 --- a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp +++ b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp @@ -1084,6 +1084,12 @@ void CanonicalizeOSSALifetime::rewriteLifetimes() { bool CanonicalizeOSSALifetime::canonicalizeValueLifetime(SILValue def) { LivenessState livenessState(*this, def); + // Don't canonicalize the lifetimes of values of move-only type. According to + // language rules, they are fixed. + if (def->getType().isMoveOnly()) { + return false; + } + // Step 1: Compute liveness. if (!computeLiveness()) { LLVM_DEBUG(llvm::dbgs() << "Failed to compute liveness boundary!\n"); diff --git a/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil b/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil index 3bff1c200b14e..0b17d1b7bab31 100644 --- a/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil +++ b/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil @@ -2,6 +2,9 @@ class C {} +@_moveOnly struct MoS {} +@_moveOnly struct MoE {} + // When access scopes are respected, the lifetime which previously extended // beyond the access scope still extends beyond it. // CHECK-LABEL: begin running test 1 of 2 on retract_value_lifetime_into_access_scope_when_access_scopes_not_respected: canonicalize-ossa-lifetime with: true, false, true, @trace @@ -45,3 +48,46 @@ bb0(%addr : $*C): %retval = tuple () return %retval : $() } + +sil @empty : $@convention(thin) () -> () { +[global: ] +bb0: + %0 = tuple () + return %0 : $() +} + +// Even though the apply of %empty is not a deinit barrier, verify that the +// destroy is not hoisted, because MoS is move-only. +// CHECK-LABEL: begin running test {{.*}} on dont_move_destroy_value_of_moveonly_struct: canonicalize-ossa-lifetime with: true, false, true, @argument +// CHECK-LABEL: sil [ossa] @dont_move_destroy_value_of_moveonly_struct : {{.*}} { +// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : +// CHECK: apply +// CHECK: destroy_value [[INSTANCE]] +// CHECK-LABEL: } // end sil function 'dont_move_destroy_value_of_moveonly_struct' +// CHECK-LABEL: end running test {{.*}} on dont_move_destroy_value_of_moveonly_struct: canonicalize-ossa-lifetime with: true, false, true, @argument +sil [ossa] @dont_move_destroy_value_of_moveonly_struct : $@convention(thin) (@owned MoS) -> () { +entry(%instance : @owned $MoS): + test_specification "canonicalize-ossa-lifetime true false true @argument" + %empty = function_ref @empty : $@convention(thin) () -> () + apply %empty() : $@convention(thin) () -> () + destroy_value %instance : $MoS + %retval = tuple () + return %retval : $() +} + +// CHECK-LABEL: begin running test {{.*}} on dont_move_destroy_value_of_moveonly_enum: canonicalize-ossa-lifetime with: true, false, true, @argument +// CHECK-LABEL: sil [ossa] @dont_move_destroy_value_of_moveonly_enum : {{.*}} { +// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : +// CHECK: apply +// CHECK: destroy_value [[INSTANCE]] +// CHECK-LABEL: } // end sil function 'dont_move_destroy_value_of_moveonly_enum' +// CHECK-LABEL: end running test {{.*}} on dont_move_destroy_value_of_moveonly_enum: canonicalize-ossa-lifetime with: true, false, true, @argument +sil [ossa] @dont_move_destroy_value_of_moveonly_enum : $@convention(thin) (@owned MoE) -> () { +entry(%instance : @owned $MoE): + test_specification "canonicalize-ossa-lifetime true false true @argument" + %empty = function_ref @empty : $@convention(thin) () -> () + apply %empty() : $@convention(thin) () -> () + destroy_value %instance : $MoE + %retval = tuple () + return %retval : $() +} diff --git a/test/SILOptimizer/hoist_destroy_addr.sil b/test/SILOptimizer/hoist_destroy_addr.sil index 9ec2aa2f038b4..0131014cffce0 100644 --- a/test/SILOptimizer/hoist_destroy_addr.sil +++ b/test/SILOptimizer/hoist_destroy_addr.sil @@ -79,6 +79,9 @@ struct STXXITXXII { var i: I } +@_moveOnly struct MoS {} +@_moveOnly struct MoE {} + sil @unknown : $@convention(thin) () -> () sil @use_S : $@convention(thin) (@in_guaranteed S) -> () @@ -1145,3 +1148,34 @@ entry(%addr : $*X): %retval = tuple () return %retval : $() } + +// Even though the apply of %empty is not a deinit barrier (c.f. +// hoist_over_apply_of_non_barrier_fn), verify that the destroy_addr is not +// hoisted, because MoS is move-only. +// CHECK-LABEL: sil [ossa] @dont_move_destroy_addr_of_moveonly_struct : {{.*}} { +// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : +// CHECK: apply +// CHECK: destroy_addr [[ADDR]] +// CHECK-LABEL: } // end sil function 'dont_move_destroy_addr_of_moveonly_struct' +sil [ossa] @dont_move_destroy_addr_of_moveonly_struct : $@convention(thin) (@in MoS) -> () { +entry(%addr : $*MoS): + %empty = function_ref @empty : $@convention(thin) () -> () + apply %empty() : $@convention(thin) () -> () + destroy_addr %addr : $*MoS + %retval = tuple () + return %retval : $() +} + +// CHECK-LABEL: sil [ossa] @dont_move_destroy_addr_of_moveonly_enum : {{.*}} { +// CHECK: {{bb[0-9]+}}([[ADDR:%[^,]+]] : +// CHECK: apply +// CHECK: destroy_addr [[ADDR]] +// CHECK-LABEL: } // end sil function 'dont_move_destroy_addr_of_moveonly_enum' +sil [ossa] @dont_move_destroy_addr_of_moveonly_enum : $@convention(thin) (@in MoE) -> () { +entry(%addr : $*MoE): + %empty = function_ref @empty : $@convention(thin) () -> () + apply %empty() : $@convention(thin) () -> () + destroy_addr %addr : $*MoE + %retval = tuple () + return %retval : $() +}