diff --git a/lib/SILOptimizer/Transforms/DestroyAddrHoisting.cpp b/lib/SILOptimizer/Transforms/DestroyAddrHoisting.cpp
index 4feb70403f429..94598b56c84aa 100644
--- a/lib/SILOptimizer/Transforms/DestroyAddrHoisting.cpp
+++ b/lib/SILOptimizer/Transforms/DestroyAddrHoisting.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 f9fda092cbea2..85d0a7e33e8e2 100644
--- a/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp
+++ b/lib/SILOptimizer/Utils/CanonicalizeOSSALifetime.cpp
@@ -1156,6 +1156,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 2f36344f108d6..7d2f7425b1f25 100644
--- a/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil
+++ b/test/SILOptimizer/canonicalize_ossa_lifetime_unit.sil
@@ -5,6 +5,9 @@ sil @getOwned : $@convention(thin) () -> @owned C
 sil @barrier : $@convention(thin) () -> ()
 struct S {}
 
+@_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
@@ -134,3 +137,46 @@ exit(%phi : @owned $C, %typhi : $S):
   %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 71e7099d13d33..837203379b38a 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 : $()
+}