diff --git a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp index 589674f134ad9..e5cf557d72a81 100644 --- a/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp +++ b/lib/SILOptimizer/SILCombiner/SILCombinerMiscVisitors.cpp @@ -1118,12 +1118,17 @@ SILInstruction *SILCombiner::visitUncheckedTakeEnumDataAddrInst( return nullptr; bool onlyLoads = true; + bool anyLoadCopies = false; bool onlyDestroys = true; for (auto U : getNonDebugUses(tedai)) { // Check if it is load. If it is not a load, bail... if (!isa(U->getUser()) && !isa(U->getUser())) onlyLoads = false; + if (auto *li = dyn_cast(U->getUser())) + if (li->getOwnershipQualifier() == LoadOwnershipQualifier::Copy) + anyLoadCopies = true; + // If we have a load_borrow, perform an additional check that we do not have // any reborrow uses. We do not handle reborrows in this optimization. if (auto *lbi = dyn_cast(U->getUser())) { @@ -1166,6 +1171,11 @@ SILInstruction *SILCombiner::visitUncheckedTakeEnumDataAddrInst( if (tedai->getOperand()->getType().isAddressOnly(*tedai->getFunction())) return nullptr; + // If the enum is noncopyable and any loads cause copies, the transformation + // would be illegal because it would introduce a copy of the noncopyable enum. + if (tedai->getOperand()->getType().isMoveOnly() && anyLoadCopies) + return nullptr; + // Grab the EnumAddr. SILLocation loc = tedai->getLoc(); Builder.setCurrentDebugScope(tedai->getDebugScope()); diff --git a/test/SILOptimizer/sil_combine_ossa.sil b/test/SILOptimizer/sil_combine_ossa.sil index 291b1d716d3fb..80066dfefed34 100644 --- a/test/SILOptimizer/sil_combine_ossa.sil +++ b/test/SILOptimizer/sil_combine_ossa.sil @@ -102,6 +102,14 @@ struct MoveOnlyStruct: ~Copyable { var value: MyInt } +enum NoncopyableEnum : ~Copyable { +case i(CopyableStruct) +} + +struct CopyableStruct { + var guts: AnyObject +} + ////////////////////// // Simple DCE Tests // ////////////////////// @@ -5530,3 +5538,44 @@ bb0: return %6 } +// unchecked_take_enum_data_addr of a noncopyable base must not be combined if +// there are any load [copy] users. +// CHECK-LABEL: sil [ossa] @no_combine_uteda_load_copy_noncopyable {{.*}} { +// CHECK: unchecked_take_enum_data_addr +// CHECK-LABEL: } // end sil function 'no_combine_uteda_load_copy_noncopyable' +sil [ossa] @no_combine_uteda_load_copy_noncopyable : $@convention(thin) (@in NoncopyableEnum) -> () { +entry(%e_addr : $*NoncopyableEnum): + %i_addr = unchecked_take_enum_data_addr %e_addr : $*NoncopyableEnum, #NoncopyableEnum.i!enumelt + %i_copy = load [copy] %i_addr : $*CopyableStruct + apply undef(%i_copy) : $@convention(thin) (@owned CopyableStruct) -> () + %i = load [take] %i_addr : $*CopyableStruct + apply undef(%i) : $@convention(thin) (@owned CopyableStruct) -> () + return undef : $() +} + +// unchecked_take_enum_data_addr of a noncopyable base should be combined if +// there are only load [take] users. +// CHECK-LABEL: sil [ossa] @combine_uteda_load_take_noncopyable {{.*}} { +// CHECK-NOT: unchecked_take_enum_data_addr +// CHECK-LABEL: } // end sil function 'combine_uteda_load_take_noncopyable' +sil [ossa] @combine_uteda_load_take_noncopyable : $@convention(thin) (@in NoncopyableEnum) -> () { +entry(%e_addr : $*NoncopyableEnum): + %i_addr = unchecked_take_enum_data_addr %e_addr : $*NoncopyableEnum, #NoncopyableEnum.i!enumelt + %i = load [take] %i_addr : $*CopyableStruct + apply undef(%i) : $@convention(thin) (@owned CopyableStruct) -> () + return undef : $() +} + +// unchecked_take_enum_data_addr of a noncopyable base should be combined if +// there are only load_borrow users. +// CHECK-LABEL: sil [ossa] @combine_uteda_load_borrow_noncopyable {{.*}} { +// CHECK-NOT: unchecked_take_enum_data_addr +// CHECK-LABEL: } // end sil function 'combine_uteda_load_borrow_noncopyable' +sil [ossa] @combine_uteda_load_borrow_noncopyable : $@convention(thin) (@in_guaranteed NoncopyableEnum) -> () { +entry(%e_addr : $*NoncopyableEnum): + %i_addr = unchecked_take_enum_data_addr %e_addr : $*NoncopyableEnum, #NoncopyableEnum.i!enumelt + %i = load_borrow %i_addr : $*CopyableStruct + apply undef(%i) : $@convention(thin) (@guaranteed CopyableStruct) -> () + end_borrow %i : $CopyableStruct + return undef : $() +}