Merge pull request #16962 from gottesmm/swift-4.2-branch-rdar40332620
[definite-init] Treat destroys of mark_uninitialized [var] container …
diff --git a/lib/SIL/SILVerifier.cpp b/lib/SIL/SILVerifier.cpp
index c044f2b..2bf1250 100644
--- a/lib/SIL/SILVerifier.cpp
+++ b/lib/SIL/SILVerifier.cpp
@@ -1749,18 +1749,34 @@
require(MU->getModule().getStage() == SILStage::Raw,
"mark_uninitialized instruction can only exist in raw SIL");
require(Src->getType().isAddress() ||
- Src->getType()
- .getSwiftRValueType()
- ->getClassOrBoundGenericClass() ||
- Src->getType().getAs<SILBoxType>(),
+ Src->getType().getClassOrBoundGenericClass() ||
+ Src->getType().getAs<SILBoxType>(),
"mark_uninitialized must be an address, class, or box type");
require(Src->getType() == MU->getType(),"operand and result type mismatch");
// FIXME: When the work to force MUI to be on Allocations/SILArguments
// complete, turn on this assertion.
-#if 0
- require(isa<AllocationInst>(Src) || isa<SILArgument>(Src),
- "Mark Uninitialized should always be on the storage location");
-#endif
+ require(isa<AllocationInst>(Src)
+ || isa<GlobalAddrInst>(Src)
+ // TODO: Should we support SILUndef on mark_uninitialized? We
+ // currently have a test that verifies this behavior, but it seems
+ // like this would always be a bug due to the implications around
+ // the code in DI. This just bakes in the current behavior.
+ || isa<SILUndef>(Src)
+ // We allow SILArguments to support the case of initializing
+ // initializers. In such a case, the underlying case is allocated
+ // outside by the allocating initializer and we pass in the to be
+ // initialized value as a SILArgument.
+ || isa<SILArgument>(Src)
+ // FIXME: Once the MarkUninitializedFixup pass is eliminated,
+ // mark_uninitialized should never be applied to a project_box. So
+ // at that point, this should be eliminated.
+ || isa<ProjectBoxInst>(Src)
+ // FIXME: We only support pointer to address here to not break LLDB. It is
+ // important that long term we get rid of this since this is a situation
+ // where LLDB is breaking SILGen/DI invariants by not creating a new
+ // independent stack location for the pointer to address.
+ || isa<PointerToAddressInst>(Src),
+ "Mark Uninitialized must be applied to a storage location");
}
void checkMarkUninitializedBehaviorInst(MarkUninitializedBehaviorInst *MU) {
diff --git a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp
index abd4e44..94e35c6 100644
--- a/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp
+++ b/lib/SILOptimizer/Mandatory/DIMemoryUseCollectorOwnership.cpp
@@ -28,7 +28,42 @@
using namespace ownership;
//===----------------------------------------------------------------------===//
-// DIMemoryObjectInfo Implementation
+// Utility
+//===----------------------------------------------------------------------===//
+
+static void gatherDestroysOfContainer(const MarkUninitializedInst *MUI,
+ DIElementUseInfo &UseInfo) {
+ // The MUI must be used on an alloc_box, alloc_stack, or global_addr. If we
+ // have an alloc_stack or a global_addr, there is nothing further to do.
+ if (isa<AllocStackInst>(MUI->getOperand()) ||
+ isa<GlobalAddrInst>(MUI->getOperand()) ||
+ isa<SILArgument>(MUI->getOperand()) ||
+ // FIXME: We only support pointer to address here to not break LLDB. It is
+ // important that long term we get rid of this since this is a situation
+ // where LLDB is breaking SILGen/DI invariants by not creating a new
+ // independent stack location for the pointer to address.
+ isa<PointerToAddressInst>(MUI->getOperand()))
+ return;
+
+ // Otherwise, we assume that we have a project_box. This is a hard cast to
+ // ensure that we catch any new patterns emitted by SILGen and assert.
+ auto *PBI = cast<ProjectBoxInst>(MUI->getOperand());
+ auto *ABI = cast<AllocBoxInst>(PBI->getOperand());
+
+ // Treat destroys of the container as load+destroys of the original value.
+ //
+ // TODO: This should really be tracked separately from other destroys so that
+ // we distinguish the lifetime of the container from the value itself.
+ for (auto *Op : ABI->getUses()) {
+ SILInstruction *User = Op->getUser();
+ if (isa<StrongReleaseInst>(User) || isa<DestroyValueInst>(User)) {
+ UseInfo.trackDestroy(User);
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// DIMemoryObjectInfo Implementation
//===----------------------------------------------------------------------===//
static unsigned getElementCountRec(SILModule &Module, SILType T,
@@ -511,6 +546,7 @@
}
collectUses(TheMemory.MemoryInst, 0);
+ gatherDestroysOfContainer(TheMemory.MemoryInst, UseInfo);
}
void trackUse(DIMemoryUse Use) { UseInfo.trackUse(Use); }
@@ -1610,20 +1646,8 @@
assert(StoresOfArgumentToSelf == 1 &&
"The 'self' argument should have been stored into the box exactly once");
- // The MUI must be used on an alloc_box or alloc_stack instruction. If we have
- // an alloc_stack, there is nothing further to do.
- if (isa<AllocStackInst>(MUI->getOperand()))
- return;
-
- auto *PBI = cast<ProjectBoxInst>(MUI->getOperand());
- auto *ABI = cast<AllocBoxInst>(PBI->getOperand());
-
- for (auto Op : ABI->getUses()) {
- SILInstruction *User = Op->getUser();
- if (isa<StrongReleaseInst>(User) || isa<DestroyValueInst>(User)) {
- UseInfo.trackDestroy(User);
- }
- }
+ // Gather the uses of the
+ gatherDestroysOfContainer(MUI, UseInfo);
}
void DelegatingClassInitElementUseCollector::
diff --git a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp
index defb78b..32c4bb6 100644
--- a/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp
+++ b/lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp
@@ -496,6 +496,10 @@
void processUninitializedRelease(SILInstruction *Release,
bool consumed,
SILBasicBlock::iterator InsertPt);
+ void processUninitializedReleaseOfBox(AllocBoxInst *ABI,
+ SILInstruction *Release,
+ bool consumed,
+ SILBasicBlock::iterator InsertPt);
void deleteDeadRelease(unsigned ReleaseID);
void processNonTrivialRelease(unsigned ReleaseID);
@@ -1857,56 +1861,68 @@
assert(isa<StoreInst>(Inst) && "Unknown store instruction!");
}
+void LifetimeChecker::processUninitializedReleaseOfBox(
+ AllocBoxInst *ABI, SILInstruction *Release, bool consumed,
+ SILBasicBlock::iterator InsertPt) {
+ assert(ABI == Release->getOperand(0));
+ SILBuilderWithScope B(Release);
+ B.setInsertionPoint(InsertPt);
+ Destroys.push_back(B.createDeallocBox(Release->getLoc(), ABI));
+}
+
void LifetimeChecker::processUninitializedRelease(SILInstruction *Release,
bool consumed,
SILBasicBlock::iterator InsertPt) {
// If this is an early release of a class instance, we need to emit a
// dealloc_partial_ref to free the memory. If this is a derived class, we
// may have to do a load of the 'self' box to get the class reference.
- if (TheMemory.isClassInitSelf()) {
- auto Loc = Release->getLoc();
-
- SILBuilderWithScope B(Release);
- B.setInsertionPoint(InsertPt);
-
- SILValue Pointer = Release->getOperand(0);
-
- // If we see an alloc_box as the pointer, then we're deallocating a 'box'
- // for self. Make sure we're using its address result, not its refcount
- // result, and make sure that the box gets deallocated (not released)
- // since the pointer it contains will be manually cleaned up.
- auto *ABI = dyn_cast<AllocBoxInst>(Release->getOperand(0));
- if (ABI)
- Pointer = getOrCreateProjectBox(ABI, 0);
-
- if (!consumed) {
- if (Pointer->getType().isAddress())
- Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take);
-
- auto MetatypeTy = CanMetatypeType::get(
- TheMemory.MemorySILType.getSwiftRValueType(),
- MetatypeRepresentation::Thick);
- auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy);
- SILValue Metatype;
-
- // In an inherited convenience initializer, we must use the dynamic
- // type of the object since nothing is initialized yet.
- if (TheMemory.isDelegatingInit())
- Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer);
- else
- Metatype = B.createMetatype(Loc, SILMetatypeTy);
-
- // We've already destroyed any instance variables initialized by this
- // constructor, now destroy instance variables initialized by subclass
- // constructors that delegated to us, and finally free the memory.
- B.createDeallocPartialRef(Loc, Pointer, Metatype);
+ if (!TheMemory.isClassInitSelf()) {
+ if (auto *ABI = dyn_cast<AllocBoxInst>(Release->getOperand(0))) {
+ return processUninitializedReleaseOfBox(ABI, Release, consumed, InsertPt);
}
-
- // dealloc_box the self box if necessary.
- if (ABI) {
- auto DB = B.createDeallocBox(Loc, ABI);
- Destroys.push_back(DB);
- }
+ return;
+ }
+
+ auto Loc = Release->getLoc();
+
+ SILBuilderWithScope B(Release);
+ B.setInsertionPoint(InsertPt);
+
+ SILValue Pointer = Release->getOperand(0);
+
+ // If we see an alloc_box as the pointer, then we're deallocating a 'box' for
+ // self. Make sure that the box gets deallocated (not released) since the
+ // pointer it contains will be manually cleaned up.
+ auto *ABI = dyn_cast<AllocBoxInst>(Release->getOperand(0));
+ if (ABI)
+ Pointer = getOrCreateProjectBox(ABI, 0);
+
+ if (!consumed) {
+ if (Pointer->getType().isAddress())
+ Pointer = B.createLoad(Loc, Pointer, LoadOwnershipQualifier::Take);
+
+ auto MetatypeTy = CanMetatypeType::get(TheMemory.MemorySILType.getASTType(),
+ MetatypeRepresentation::Thick);
+ auto SILMetatypeTy = SILType::getPrimitiveObjectType(MetatypeTy);
+ SILValue Metatype;
+
+ // In an inherited convenience initializer, we must use the dynamic
+ // type of the object since nothing is initialized yet.
+ if (TheMemory.isDelegatingInit())
+ Metatype = B.createValueMetatype(Loc, SILMetatypeTy, Pointer);
+ else
+ Metatype = B.createMetatype(Loc, SILMetatypeTy);
+
+ // We've already destroyed any instance variables initialized by this
+ // constructor, now destroy instance variables initialized by subclass
+ // constructors that delegated to us, and finally free the memory.
+ B.createDeallocPartialRef(Loc, Pointer, Metatype);
+ }
+
+ // dealloc_box the self box if necessary.
+ if (ABI) {
+ auto DB = B.createDeallocBox(Loc, ABI);
+ Destroys.push_back(DB);
}
}
diff --git a/test/SILOptimizer/definite_init_markuninitialized_var.sil b/test/SILOptimizer/definite_init_markuninitialized_var.sil
index f4958e1..f927a84 100644
--- a/test/SILOptimizer/definite_init_markuninitialized_var.sil
+++ b/test/SILOptimizer/definite_init_markuninitialized_var.sil
@@ -542,3 +542,76 @@
%23 = tuple () // user: %24
return %23 : $() // id: %24
}
+// CHECK: } // end sil function 'conditionalInitOrAssign'
+
+// Make sure that we use a dealloc_box on the path where we do not assign.
+//
+// CHECK-LABEL: sil @conditionalInitOrAssignAllocBox : $@convention(thin) () -> () {
+// CHECK: bb0:
+// CHECK: [[BOX:%.*]] = alloc_box
+//
+// CHECK: bb1:
+// CHECK: destroy_value [[BOX]]
+// CHECK: br bb3
+//
+// CHECK: bb2:
+// CHECK: dealloc_box [[BOX]]
+// CHECK: br bb3
+//
+// CHECK: bb3:
+// CHECK-NEXT: tuple
+// CHECK-NEXT: return
+// CHECK: } // end sil function 'conditionalInitOrAssignAllocBox'
+sil @conditionalInitOrAssignAllocBox : $@convention(thin) () -> () {
+bb0:
+ %box = alloc_box $<τ_0_0> { var τ_0_0 } <SomeClass>
+ %5 = project_box %box : $<τ_0_0> { var τ_0_0 } <SomeClass>, 0
+ %7 = mark_uninitialized [var] %5 : $*SomeClass
+ %2 = function_ref @getSomeClass : $@convention(thin) () -> @owned SomeClass
+ cond_br undef, bb1, bb2
+
+// Value is stored into box and then destroyed.
+bb1:
+ %12 = apply %2() : $@convention(thin) () -> @owned SomeClass
+ assign %12 to %7 : $*SomeClass
+ destroy_value %box : $<τ_0_0> { var τ_0_0 } <SomeClass>
+ br bb3
+
+bb2:
+ destroy_value %box : $<τ_0_0> { var τ_0_0 } <SomeClass>
+ br bb3
+
+bb3:
+ %9999 = tuple()
+ return %9999 : $()
+}
+
+// LLDB uses mark_uninitialized [var] in an unsupported way via an address to
+// pointer. LLDB shouldn't do this, but until it is removed and validated we
+// need a test for this.
+// CHECK-LABEL: sil @lldb_unsupported_markuninitialized_testcase : $@convention(thin) (UnsafeMutablePointer<Any>) -> () {
+// CHECK-NOT: mark_uninitialized [var]
+// CHECK: } // end sil function 'lldb_unsupported_markuninitialized_testcase'
+sil @lldb_unsupported_markuninitialized_testcase : $@convention(thin) (UnsafeMutablePointer<Any>) -> () {
+bb0(%0 : @trivial $UnsafeMutablePointer<Any>):
+ %2 = struct_extract %0 : $UnsafeMutablePointer<Any>, #UnsafeMutablePointer._rawValue
+ %3 = integer_literal $Builtin.Int64, 8
+ %4 = index_raw_pointer %2 : $Builtin.RawPointer, %3 : $Builtin.Int64
+ %5 = pointer_to_address %4 : $Builtin.RawPointer to [strict] $*Builtin.RawPointer
+ %6 = load [trivial] %5 : $*Builtin.RawPointer
+ %7 = pointer_to_address %6 : $Builtin.RawPointer to [strict] $*Error
+ %8 = mark_uninitialized [var] %7 : $*Error
+ %9 = struct_extract %0 : $UnsafeMutablePointer<Any>, #UnsafeMutablePointer._rawValue
+ %10 = integer_literal $Builtin.Int64, 0
+ %11 = index_raw_pointer %9 : $Builtin.RawPointer, %10 : $Builtin.Int64
+ %12 = pointer_to_address %11 : $Builtin.RawPointer to [strict] $*Builtin.RawPointer
+ %13 = load [trivial] %12 : $*Builtin.RawPointer
+ %14 = pointer_to_address %13 : $Builtin.RawPointer to [strict] $*@thick SomeClass.Type
+ %15 = mark_uninitialized [var] %14 : $*@thick SomeClass.Type
+ %16 = metatype $@thick SomeClass.Type
+ %17 = begin_access [modify] [unsafe] %15 : $*@thick SomeClass.Type
+ assign %16 to %17 : $*@thick SomeClass.Type
+ end_access %17 : $*@thick SomeClass.Type
+ %9999 = tuple()
+ return %9999 : $()
+}