Skip to content

Commit

Permalink
Runtime: Invoke objc_destructInstance before deallocating class insta…
Browse files Browse the repository at this point in the history
…nces.

This is necessary to clean up associated objects and ObjC weak references to the object. rdar://problem/18637774

Swift SVN r23068
  • Loading branch information
jckarter committed Nov 2, 2014
1 parent efa166c commit 0885c56
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 5 deletions.
17 changes: 17 additions & 0 deletions include/swift/Runtime/HeapObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,23 @@ extern "C" size_t swift_retainCount(HeapObject *object);
extern "C" void swift_deallocObject(HeapObject *object, size_t allocatedSize,
size_t allocatedAlignMask);

/// Deallocate the given class instance; it was returned by swift_allocObject
/// and possibly used as an Objective-C class instance, but is otherwise in an
/// unknown state.
///
/// \param object - never null
/// \param allocatedSize - the allocated size of the object from the
/// program's perspective, i.e. the value
/// \param allocatedAlignMask - the alignment requirement that was passed
/// to allocObject
///
/// POSSIBILITIES: It may be useful to have a variant which
/// requires the object to have been fully zeroed from offsets
/// sizeof(SwiftHeapObject) to allocatedSize.
extern "C" void swift_deallocClassInstance(HeapObject *object,
size_t allocatedSize,
size_t allocatedAlignMask);

/// Deallocate the given memory allocated by swift_allocBox; it was returned
/// by swift_allocBox but is otherwise in an unknown state. The given Metadata
/// pointer must be the same metadata pointer that was passed to swift_allocBox
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ void irgen::emitClassDeallocation(IRGenFunction &IGF, SILType selfType,
size, alignMask);

selfValue = IGF.Builder.CreateBitCast(selfValue, IGF.IGM.RefCountedPtrTy);
emitDeallocateHeapObject(IGF, selfValue, size, alignMask);
emitDeallocateClassInstance(IGF, selfValue, size, alignMask);
}

llvm::Constant *irgen::tryEmitClassConstantFragileInstanceSize(
Expand Down
10 changes: 10 additions & 0 deletions lib/IRGen/GenHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,16 @@ void irgen::emitDeallocateHeapObject(IRGenFunction &IGF,
object, size, alignMask);
}

void irgen::emitDeallocateClassInstance(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *size,
llvm::Value *alignMask) {
// FIXME: We should call a fast deallocator for heap objects with
// known size.
IGF.Builder.CreateCall3(IGF.IGM.getDeallocClassInstanceFn(),
object, size, alignMask);
}

/// Create the destructor function for a layout.
/// TODO: give this some reasonable name and possibly linkage.
static llvm::Function *createDtorFn(IRGenModule &IGM,
Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/GenHeap.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ void emitDeallocateHeapObject(IRGenFunction &IGF,
llvm::Value *size,
llvm::Value *alignMask);

/// Emit a class instance deallocation.
void emitDeallocateClassInstance(IRGenFunction &IGF,
llvm::Value *object,
llvm::Value *size,
llvm::Value *alignMask);

} // end namespace irgen
} // end namespace swift

Expand Down
6 changes: 6 additions & 0 deletions lib/IRGen/RuntimeFunctions.def
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ FUNCTION(DeallocObject, swift_deallocObject, RuntimeCC,
ARGS(RefCountedPtrTy, SizeTy, SizeTy),
ATTRS(NoUnwind))

// void swift_deallocClassInstance(RefCounted *obj, size_t size, size_t alignMask);
FUNCTION(DeallocClassInstance, swift_deallocClassInstance, RuntimeCC,
RETURNS(VoidTy),
ARGS(RefCountedPtrTy, SizeTy, SizeTy),
ATTRS(NoUnwind))

// void *swift_slowAlloc(size_t size, size_t alignMask);
FUNCTION(SlowAlloc, swift_slowAlloc, RuntimeCC,
RETURNS(Int8PtrTy),
Expand Down
17 changes: 15 additions & 2 deletions stdlib/runtime/HeapObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
#include <cstdlib>
#include <unistd.h>
#include "../shims/RuntimeShims.h"
#if SWIFT_OBJC_INTEROP
# include <objc/objc-runtime.h>
#endif

using namespace swift;

Expand Down Expand Up @@ -336,8 +339,18 @@ void swift::_swift_deallocClassInstance(HeapObject *self) {
assert(metadata->isClassObject());
auto classMetadata = static_cast<const ClassMetadata*>(metadata);
assert(classMetadata->isTypeMetadata());
swift_deallocObject(self, classMetadata->getInstanceSize(),
classMetadata->getInstanceAlignMask());
swift_deallocClassInstance(self, classMetadata->getInstanceSize(),
classMetadata->getInstanceAlignMask());
}

void swift::swift_deallocClassInstance(HeapObject *object, size_t allocatedSize,
size_t allocatedAlignMask) {
#if SWIFT_OBJC_INTEROP
// We need to let the ObjC runtime clean up any associated objects or weak
// references associated with this object.
objc_destructInstance((id)object);
#endif
swift_deallocObject(object, allocatedSize, allocatedAlignMask);
}

void swift::swift_deallocObject(HeapObject *object, size_t allocatedSize,
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/class.sil
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ bb0(%0 : $C):
%2 = apply %1(%0) : $@cc(method) @thin (@owned C) -> @owned Builtin.NativeObject // user: %3
%3 = unchecked_ref_cast %2 : $Builtin.NativeObject to $C // user: %4
// CHECK-NEXT: [[SELF:%[a-zA-Z0-9]+]] = bitcast [[C_CLASS]]* [[SELF_OBJ]] to [[REF]]*
// CHECK-NEXT: call void @swift_deallocObject([[REF]]* [[SELF]], i64 16, i64 7)
// CHECK-NEXT: call void @swift_deallocClassInstance([[REF]]* [[SELF]], i64 16, i64 7)
dealloc_ref %3 : $C // id: %4
// CHECK-NEXT: ret void
%5 = tuple () // user: %6
Expand Down
2 changes: 1 addition & 1 deletion test/IRGen/deallocate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ class CustomDeallocator {
// CHECK-NEXT: [[SIZE:%.*]] = extractvalue { i64, i64 } [[T3]], 0
// CHECK-NEXT: [[ALIGNMASK:%.*]] = extractvalue { i64, i64 } [[T3]], 1
// CHECK-NEXT: [[T4:%.*]] = bitcast [[CD]]* [[T1]] to [[OBJECT]]*
// CHECK-NEXT: call void @swift_deallocObject([[OBJECT]]* [[T4]], i64 [[SIZE]], i64 [[ALIGNMASK]])
// CHECK-NEXT: call void @swift_deallocClassInstance([[OBJECT]]* [[T4]], i64 [[SIZE]], i64 [[ALIGNMASK]])
// CHECK-NEXT: ret void
24 changes: 24 additions & 0 deletions test/Interpreter/SDK/objc_dealloc.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %target-run-simple-swift | FileCheck %s

import Foundation

// Check that ObjC associated objects are cleaned up when attached to native
// Swift objects.

class Root {
deinit { println("deallocating root") }
}

class Associated {
deinit { println("deallocating associated") }
}

var token: Int8 = 0

autoreleasepool {
let root = Root()
objc_setAssociatedObject(root, &token, Associated(),
objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC))
}
// CHECK: deallocating root
// CHECK-NEXT: deallocating associated

0 comments on commit 0885c56

Please sign in to comment.