Small MM refactor, ignore acyclic and frozen objects in cycle collector. (#2354)
This commit is contained in:
+43
-3
@@ -6,27 +6,66 @@
|
|||||||
package org.jetbrains.kotlin.backend.konan.llvm
|
package org.jetbrains.kotlin.backend.konan.llvm
|
||||||
|
|
||||||
import llvm.*
|
import llvm.*
|
||||||
|
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
|
import org.jetbrains.kotlin.backend.konan.computePrimitiveBinaryTypeOrNull
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
import org.jetbrains.kotlin.backend.konan.descriptors.*
|
||||||
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
import org.jetbrains.kotlin.backend.konan.irasdescriptors.*
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||||
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
|
import org.jetbrains.kotlin.ir.util.isAnnotationClass
|
||||||
|
import org.jetbrains.kotlin.ir.util.isInterface
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
|
||||||
|
|
||||||
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
||||||
|
|
||||||
|
private val acyclicCache = mutableMapOf<IrType, Boolean>()
|
||||||
|
private val safeAcyclicFieldTypes = setOf(
|
||||||
|
context.irBuiltIns.stringClass,
|
||||||
|
context.irBuiltIns.booleanClass, context.irBuiltIns.charClass,
|
||||||
|
context.irBuiltIns.byteClass, context.irBuiltIns.shortClass, context.irBuiltIns.intClass,
|
||||||
|
context.irBuiltIns.longClass,
|
||||||
|
context.irBuiltIns.floatClass,context.irBuiltIns.doubleClass) +
|
||||||
|
context.ir.symbols.primitiveArrays.values +
|
||||||
|
context.ir.symbols.unsignedArrays.values
|
||||||
|
|
||||||
|
// TODO: extend logic here by taking into account final acyclic classes.
|
||||||
|
private fun checkAcyclicFieldType(type: IrType): Boolean = acyclicCache.getOrPut(type) {
|
||||||
|
when {
|
||||||
|
type.isInterface() -> false
|
||||||
|
type.computePrimitiveBinaryTypeOrNull() != null -> true
|
||||||
|
else -> {
|
||||||
|
val classifier = type.classifierOrNull
|
||||||
|
(classifier != null && classifier in safeAcyclicFieldTypes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAcyclicClass(classDescriptor: ClassDescriptor): Boolean = when {
|
||||||
|
classDescriptor.symbol == context.ir.symbols.array -> false
|
||||||
|
classDescriptor.isArray -> true
|
||||||
|
context.llvmDeclarations.forClass(classDescriptor).fields.all { checkAcyclicFieldType(it.type) } -> true
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
private fun flagsFromClass(classDescriptor: ClassDescriptor): Int {
|
private fun flagsFromClass(classDescriptor: ClassDescriptor): Int {
|
||||||
var result = 0
|
var result = 0
|
||||||
if (classDescriptor.isFrozen)
|
if (classDescriptor.isFrozen)
|
||||||
result = result or TF_IMMUTABLE
|
result = result or TF_IMMUTABLE
|
||||||
|
// TODO: maybe perform deeper analysis to find surely acyclic types.
|
||||||
|
if (!classDescriptor.isInterface && !classDescriptor.isAbstract() && !classDescriptor.isAnnotationClass) {
|
||||||
|
if (checkAcyclicClass(classDescriptor)) {
|
||||||
|
result = result or TF_ACYCLIC
|
||||||
|
}
|
||||||
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class FieldTableRecord(val nameSignature: LocalHash, val fieldOffset: Int) :
|
private inner class FieldTableRecord(val nameSignature: LocalHash, fieldOffset: Int) :
|
||||||
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
Struct(runtime.fieldTableRecordType, nameSignature, Int32(fieldOffset))
|
||||||
|
|
||||||
inner class MethodTableRecord(val nameSignature: LocalHash, val methodEntryPoint: ConstPointer?) :
|
inner class MethodTableRecord(val nameSignature: LocalHash, methodEntryPoint: ConstPointer?) :
|
||||||
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
Struct(runtime.methodTableRecordType, nameSignature, methodEntryPoint)
|
||||||
|
|
||||||
private inner class TypeInfo(
|
private inner class TypeInfo(
|
||||||
@@ -379,3 +418,4 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const val TF_IMMUTABLE = 1
|
private const val TF_IMMUTABLE = 1
|
||||||
|
private const val TF_ACYCLIC = 2
|
||||||
|
|||||||
@@ -545,8 +545,8 @@ inline void IncrementRC(ContainerHeader* container) {
|
|||||||
container->incRefCount<Atomic>();
|
container->incRefCount<Atomic>();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Atomic>
|
template <bool Atomic, bool UseCycleCollector>
|
||||||
inline void DecrementRC(ContainerHeader* container, bool useCycleCollector) {
|
inline void DecrementRC(ContainerHeader* container) {
|
||||||
if (container->decRefCount<Atomic>() == 0) {
|
if (container->decRefCount<Atomic>() == 0) {
|
||||||
FreeContainer(container);
|
FreeContainer(container);
|
||||||
}
|
}
|
||||||
@@ -561,18 +561,21 @@ inline uint32_t freeableSize(MemoryState* state) {
|
|||||||
template <bool Atomic>
|
template <bool Atomic>
|
||||||
inline void IncrementRC(ContainerHeader* container) {
|
inline void IncrementRC(ContainerHeader* container) {
|
||||||
container->incRefCount<Atomic>();
|
container->incRefCount<Atomic>();
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
container->setColorUnlessGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool Atomic>
|
template <bool Atomic, bool UseCycleCollector>
|
||||||
inline void DecrementRC(ContainerHeader* container, bool useCycleCollector) {
|
inline void DecrementRC(ContainerHeader* container) {
|
||||||
if (container->decRefCount<Atomic>() == 0) {
|
if (container->decRefCount<Atomic>() == 0) {
|
||||||
FreeContainer(container);
|
FreeContainer(container);
|
||||||
} else if (!Atomic && useCycleCollector) { // Possible root.
|
} else if (UseCycleCollector) { // Possible root.
|
||||||
// Do not use cycle collector for frozen objects, as we already detected possible cycles during
|
RuntimeAssert(!Atomic, "Cycle collector shalln't be used with shared objects yet");
|
||||||
// freezing.
|
// We do not use cycle collector for frozen objects, as we already detected
|
||||||
if (container->color() != CONTAINER_TAG_GC_PURPLE) {
|
// possible cycles during freezing.
|
||||||
container->setColor(CONTAINER_TAG_GC_PURPLE);
|
// Also do not use cycle collector for provable acyclic objects.
|
||||||
|
int color = container->color();
|
||||||
|
if (color != CONTAINER_TAG_GC_PURPLE && color != CONTAINER_TAG_GC_GREEN) {
|
||||||
|
container->setColorAssertIfGreen(CONTAINER_TAG_GC_PURPLE);
|
||||||
if (!container->buffered()) {
|
if (!container->buffered()) {
|
||||||
container->setBuffered();
|
container->setBuffered();
|
||||||
auto state = memoryState;
|
auto state = memoryState;
|
||||||
@@ -622,26 +625,34 @@ void dumpReachable(const char* prefix, const ContainerHeaderSet* roots) {
|
|||||||
#if USE_GC
|
#if USE_GC
|
||||||
|
|
||||||
void MarkRoots(MemoryState*);
|
void MarkRoots(MemoryState*);
|
||||||
void DeleteCorpses(MemoryState*);
|
|
||||||
void ScanRoots(MemoryState*);
|
void ScanRoots(MemoryState*);
|
||||||
void CollectRoots(MemoryState*);
|
void CollectRoots(MemoryState*);
|
||||||
|
void Scan(ContainerHeader* container);
|
||||||
|
|
||||||
|
#if TRACE_MEMORY
|
||||||
|
const char* colorNames[] = {"BLACK", "GRAY", "WHITE", "PURPLE", "GREEN", "ORANGE", "RED"};
|
||||||
|
#endif
|
||||||
|
|
||||||
template<bool useColor>
|
template<bool useColor>
|
||||||
void MarkGray(ContainerHeader* start) {
|
void MarkGray(ContainerHeader* start) {
|
||||||
ContainerHeaderDeque toVisit;
|
ContainerHeaderDeque toVisit;
|
||||||
toVisit.push_back(start);
|
toVisit.push_front(start);
|
||||||
|
|
||||||
while (!toVisit.empty()) {
|
while (!toVisit.empty()) {
|
||||||
auto container = toVisit.front();
|
auto* container = toVisit.front();
|
||||||
|
MEMORY_LOG("MarkGray visit %p [%s]\n", container, colorNames[container->color()]);
|
||||||
toVisit.pop_front();
|
toVisit.pop_front();
|
||||||
if (useColor) {
|
if (useColor) {
|
||||||
if (container->color() == CONTAINER_TAG_GC_GRAY) continue;
|
int color = container->color();
|
||||||
} else {
|
if (color == CONTAINER_TAG_GC_GRAY) continue;
|
||||||
if (container->marked()) continue;
|
// If see an acyclic object not being garbage - ignore it. We must properly traverse garbage, although.
|
||||||
}
|
if (color == CONTAINER_TAG_GC_GREEN && container->refCount() != 0) {
|
||||||
if (useColor) {
|
continue;
|
||||||
container->setColor(CONTAINER_TAG_GC_GRAY);
|
}
|
||||||
|
// Only garbage green object could be recolored here.
|
||||||
|
container->setColorEvenIfGreen(CONTAINER_TAG_GC_GRAY);
|
||||||
} else {
|
} else {
|
||||||
|
if (container->marked()) continue;
|
||||||
container->mark();
|
container->mark();
|
||||||
}
|
}
|
||||||
traverseContainerReferredObjects(container, [&toVisit](ObjHeader* ref) {
|
traverseContainerReferredObjects(container, [&toVisit](ObjHeader* ref) {
|
||||||
@@ -656,29 +667,29 @@ void MarkGray(ContainerHeader* start) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scan(ContainerHeader* container);
|
|
||||||
|
|
||||||
template<bool useColor>
|
template<bool useColor>
|
||||||
void ScanBlack(ContainerHeader* start) {
|
void ScanBlack(ContainerHeader* start) {
|
||||||
ContainerHeaderDeque toVisit;
|
ContainerHeaderDeque toVisit;
|
||||||
toVisit.push_back(start);
|
toVisit.push_front(start);
|
||||||
|
|
||||||
while (!toVisit.empty()) {
|
while (!toVisit.empty()) {
|
||||||
auto container = toVisit.front();
|
auto* container = toVisit.front();
|
||||||
|
MEMORY_LOG("ScanBlack visit %p [%s]\n", container, colorNames[container->color()]);
|
||||||
toVisit.pop_front();
|
toVisit.pop_front();
|
||||||
if (useColor) {
|
if (useColor) {
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
if (container->color() == CONTAINER_TAG_GC_GREEN) continue;
|
||||||
|
container->setColorAssertIfGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
} else {
|
} else {
|
||||||
container->unMark();
|
container->unMark();
|
||||||
}
|
}
|
||||||
|
|
||||||
traverseContainerReferredObjects(container, [&toVisit](ObjHeader* ref) {
|
traverseContainerReferredObjects(container, [&toVisit](ObjHeader* ref) {
|
||||||
auto childContainer = ref->container();
|
auto childContainer = ref->container();
|
||||||
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
||||||
if (!childContainer->shareable()) {
|
if (!childContainer->shareable()) {
|
||||||
childContainer->incRefCount<false>();
|
childContainer->incRefCount<false>();
|
||||||
if (useColor) {
|
if (useColor) {
|
||||||
if (childContainer->color() != CONTAINER_TAG_GC_BLACK)
|
int color = childContainer->color();
|
||||||
|
if (color != CONTAINER_TAG_GC_BLACK)
|
||||||
toVisit.push_front(childContainer);
|
toVisit.push_front(childContainer);
|
||||||
} else {
|
} else {
|
||||||
if (childContainer->marked())
|
if (childContainer->marked())
|
||||||
@@ -703,6 +714,8 @@ void MarkRoots(MemoryState* state) {
|
|||||||
for (auto container : *(state->toFree)) {
|
for (auto container : *(state->toFree)) {
|
||||||
if (isMarkedAsRemoved(container))
|
if (isMarkedAsRemoved(container))
|
||||||
continue;
|
continue;
|
||||||
|
// Acyclic containers cannot be in this list.
|
||||||
|
RuntimeCheck(container->color() != CONTAINER_TAG_GC_GREEN, "Must not be green");
|
||||||
auto color = container->color();
|
auto color = container->color();
|
||||||
auto rcIsZero = container->refCount() == 0;
|
auto rcIsZero = container->refCount() == 0;
|
||||||
if (color == CONTAINER_TAG_GC_PURPLE && !rcIsZero) {
|
if (color == CONTAINER_TAG_GC_PURPLE && !rcIsZero) {
|
||||||
@@ -710,6 +723,7 @@ void MarkRoots(MemoryState* state) {
|
|||||||
state->roots->push_back(container);
|
state->roots->push_back(container);
|
||||||
} else {
|
} else {
|
||||||
container->resetBuffered();
|
container->resetBuffered();
|
||||||
|
RuntimeAssert(color != CONTAINER_TAG_GC_GREEN, "Must not be green");
|
||||||
if (color == CONTAINER_TAG_GC_BLACK && rcIsZero) {
|
if (color == CONTAINER_TAG_GC_BLACK && rcIsZero) {
|
||||||
scheduleDestroyContainer(state, container);
|
scheduleDestroyContainer(state, container);
|
||||||
}
|
}
|
||||||
@@ -727,27 +741,34 @@ void CollectRoots(MemoryState* state) {
|
|||||||
// Here we might free some objects and call deallocation hooks on them,
|
// Here we might free some objects and call deallocation hooks on them,
|
||||||
// which in turn might call DecrementRC and trigger new GC - forbid that.
|
// which in turn might call DecrementRC and trigger new GC - forbid that.
|
||||||
state->gcSuspendCount++;
|
state->gcSuspendCount++;
|
||||||
for (auto container : *(state->roots)) {
|
for (auto* container : *(state->roots)) {
|
||||||
container->resetBuffered();
|
container->resetBuffered();
|
||||||
CollectWhite(state, container);
|
CollectWhite(state, container);
|
||||||
}
|
}
|
||||||
state->gcSuspendCount--;
|
state->gcSuspendCount--;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Scan(ContainerHeader* container) {
|
void Scan(ContainerHeader* start) {
|
||||||
if (container->color() != CONTAINER_TAG_GC_GRAY) return;
|
ContainerHeaderDeque toVisit;
|
||||||
if (container->refCount() != 0) {
|
toVisit.push_front(start);
|
||||||
ScanBlack<true>(container);
|
|
||||||
return;
|
while (!toVisit.empty()) {
|
||||||
}
|
auto* container = toVisit.front();
|
||||||
container->setColor(CONTAINER_TAG_GC_WHITE);
|
toVisit.pop_front();
|
||||||
traverseContainerReferredObjects(container, [](ObjHeader* ref) {
|
if (container->color() != CONTAINER_TAG_GC_GRAY) continue;
|
||||||
auto childContainer = ref->container();
|
if (container->refCount() != 0) {
|
||||||
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
ScanBlack<true>(container);
|
||||||
if (!childContainer->shareable()) {
|
continue;
|
||||||
Scan(childContainer);
|
}
|
||||||
}
|
container->setColorAssertIfGreen(CONTAINER_TAG_GC_WHITE);
|
||||||
});
|
traverseContainerReferredObjects(container, [&toVisit](ObjHeader* ref) {
|
||||||
|
auto* childContainer = ref->container();
|
||||||
|
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
||||||
|
if (!childContainer->shareable()) {
|
||||||
|
toVisit.push_front(childContainer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CollectWhite(MemoryState* state, ContainerHeader* start) {
|
void CollectWhite(MemoryState* state, ContainerHeader* start) {
|
||||||
@@ -755,14 +776,14 @@ void CollectWhite(MemoryState* state, ContainerHeader* start) {
|
|||||||
toVisit.push_back(start);
|
toVisit.push_back(start);
|
||||||
|
|
||||||
while (!toVisit.empty()) {
|
while (!toVisit.empty()) {
|
||||||
auto container = toVisit.front();
|
auto* container = toVisit.front();
|
||||||
toVisit.pop_front();
|
toVisit.pop_front();
|
||||||
if (container->color() != CONTAINER_TAG_GC_WHITE || container->buffered()) continue;
|
if (container->color() != CONTAINER_TAG_GC_WHITE || container->buffered()) continue;
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
container->setColorAssertIfGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
traverseContainerObjectFields(container, [state, &toVisit](ObjHeader** location) {
|
traverseContainerObjectFields(container, [state, &toVisit](ObjHeader** location) {
|
||||||
auto ref = *location;
|
auto* ref = *location;
|
||||||
if (ref == nullptr) return;
|
if (ref == nullptr) return;
|
||||||
auto childContainer = ref->container();
|
auto* childContainer = ref->container();
|
||||||
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
RuntimeAssert(!isArena(childContainer), "A reference to local object is encountered");
|
||||||
if (childContainer->shareable()) {
|
if (childContainer->shareable()) {
|
||||||
UpdateRef(location, nullptr);
|
UpdateRef(location, nullptr);
|
||||||
@@ -784,19 +805,16 @@ inline void AddRef(ContainerHeader* header) {
|
|||||||
case CONTAINER_TAG_PERMANENT:
|
case CONTAINER_TAG_PERMANENT:
|
||||||
break;
|
break;
|
||||||
case CONTAINER_TAG_NORMAL:
|
case CONTAINER_TAG_NORMAL:
|
||||||
IncrementRC<false>(header);
|
IncrementRC</* Atomic = */ false>(header);
|
||||||
break;
|
|
||||||
case CONTAINER_TAG_FROZEN:
|
|
||||||
case CONTAINER_TAG_ATOMIC:
|
|
||||||
IncrementRC<true>(header);
|
|
||||||
break;
|
break;
|
||||||
|
/* case CONTAINER_TAG_FROZEN: case CONTAINER_TAG_ATOMIC: */
|
||||||
default:
|
default:
|
||||||
RuntimeAssert(false, "unknown container type");
|
IncrementRC</* Atomic = */ true>(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void Release(ContainerHeader* header, bool useCycleCollector) {
|
inline void Release(ContainerHeader* header) {
|
||||||
// Looking at container type we may want to skip Release() totally
|
// Looking at container type we may want to skip Release() totally
|
||||||
// (non-escaping stack objects, constant objects).
|
// (non-escaping stack objects, constant objects).
|
||||||
switch (header->refCount_ & CONTAINER_TAG_MASK) {
|
switch (header->refCount_ & CONTAINER_TAG_MASK) {
|
||||||
@@ -804,14 +822,11 @@ inline void Release(ContainerHeader* header, bool useCycleCollector) {
|
|||||||
case CONTAINER_TAG_STACK:
|
case CONTAINER_TAG_STACK:
|
||||||
break;
|
break;
|
||||||
case CONTAINER_TAG_NORMAL:
|
case CONTAINER_TAG_NORMAL:
|
||||||
DecrementRC<false>(header, useCycleCollector);
|
DecrementRC</* Atomic = */ false, /* UseCyclicCollector = */ true>(header);
|
||||||
break;
|
|
||||||
case CONTAINER_TAG_FROZEN:
|
|
||||||
case CONTAINER_TAG_ATOMIC:
|
|
||||||
DecrementRC<true>(header, useCycleCollector);
|
|
||||||
break;
|
break;
|
||||||
|
/* case CONTAINER_TAG_FROZEN: case CONTAINER_TAG_ATOMIC: */
|
||||||
default:
|
default:
|
||||||
RuntimeAssert(false, "unknown container type");
|
DecrementRC</* Atomic = */ true, /* UseCyclicCollector = */ false>(header);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -951,7 +966,7 @@ void FreeContainer(ContainerHeader* container) {
|
|||||||
|
|
||||||
// And release underlying memory.
|
// And release underlying memory.
|
||||||
if (isFreeable(container)) {
|
if (isFreeable(container)) {
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
container->setColorEvenIfGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
if (!container->buffered())
|
if (!container->buffered())
|
||||||
scheduleDestroyContainer(state, container);
|
scheduleDestroyContainer(state, container);
|
||||||
}
|
}
|
||||||
@@ -1091,9 +1106,7 @@ inline void AddRef(const ObjHeader* object) {
|
|||||||
|
|
||||||
inline void ReleaseRef(const ObjHeader* object) {
|
inline void ReleaseRef(const ObjHeader* object) {
|
||||||
MEMORY_LOG("ReleaseRef on %p in %p\n", object, object->container())
|
MEMORY_LOG("ReleaseRef on %p in %p\n", object, object->container())
|
||||||
// Use cycle collector only for objects having object fields, or if container is multiobject.
|
Release(object->container());
|
||||||
auto container = object->container();
|
|
||||||
Release(container, (object->type_info()->objOffsetsCount_ > 0) || (container->objectCount() > 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddRefFromAssociatedObject(const ObjHeader* object) {
|
void AddRefFromAssociatedObject(const ObjHeader* object) {
|
||||||
@@ -1533,17 +1546,22 @@ OBJ_GETTER(AdoptStablePointer, KNativePtr pointer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if USE_GC
|
#if USE_GC
|
||||||
|
bool hasExternalRefs(ContainerHeader* start, ContainerHeaderSet* visited) {
|
||||||
bool hasExternalRefs(ContainerHeader* container, ContainerHeaderSet* visited) {
|
ContainerHeaderDeque toVisit;
|
||||||
visited->insert(container);
|
toVisit.push_back(start);
|
||||||
bool result = container->refCount() != 0;
|
while (!toVisit.empty()) {
|
||||||
traverseContainerReferredObjects(container, [&result, visited](ObjHeader* ref) {
|
auto* container = toVisit.front();
|
||||||
auto child = ref->container();
|
toVisit.pop_front();
|
||||||
if (!child->shareable() && (visited->find(child) == visited->end())) {
|
visited->insert(container);
|
||||||
result |= hasExternalRefs(child, visited);
|
if (container->refCount() != 0) return true;
|
||||||
}
|
traverseContainerReferredObjects(container, [&toVisit, visited](ObjHeader* ref) {
|
||||||
});
|
auto* child = ref->container();
|
||||||
return result;
|
if (!child->shareable() && (visited->count(child) == 0)) {
|
||||||
|
toVisit.push_front(child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1551,7 +1569,7 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
|
|||||||
#if USE_GC
|
#if USE_GC
|
||||||
if (root != nullptr) {
|
if (root != nullptr) {
|
||||||
auto state = memoryState;
|
auto state = memoryState;
|
||||||
auto container = root->container();
|
auto* container = root->container();
|
||||||
|
|
||||||
if (container->frozen())
|
if (container->frozen())
|
||||||
// We assume, that frozen objects can be safely passed and are already removed
|
// We assume, that frozen objects can be safely passed and are already removed
|
||||||
@@ -1572,12 +1590,12 @@ bool ClearSubgraphReferences(ObjHeader* root, bool checked) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: not very effecient traversal.
|
// TODO: not very efficient traversal.
|
||||||
for (auto it = state->toFree->begin(); it != state->toFree->end(); ++it) {
|
for (auto it = state->toFree->begin(); it != state->toFree->end(); ++it) {
|
||||||
auto container = *it;
|
auto container = *it;
|
||||||
if (visited.find(container) != visited.end()) {
|
if (visited.count(container) != 0) {
|
||||||
container->resetBuffered();
|
container->resetBuffered();
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
container->setColorAssertIfGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
*it = markAsRemoved(container);
|
*it = markAsRemoved(container);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1643,7 +1661,7 @@ void freezeAcyclic(ContainerHeader* rootContainer) {
|
|||||||
queue.pop_front();
|
queue.pop_front();
|
||||||
current->unMark();
|
current->unMark();
|
||||||
current->resetBuffered();
|
current->resetBuffered();
|
||||||
current->setColor(CONTAINER_TAG_GC_BLACK);
|
current->setColorUnlessGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||||
// color and similar attributes shall not be used.
|
// color and similar attributes shall not be used.
|
||||||
current->freeze();
|
current->freeze();
|
||||||
@@ -1712,7 +1730,7 @@ void freezeCyclic(ContainerHeader* rootContainer, const KStdVector<ContainerHead
|
|||||||
// Freeze component.
|
// Freeze component.
|
||||||
for (auto* container : component) {
|
for (auto* container : component) {
|
||||||
container->resetBuffered();
|
container->resetBuffered();
|
||||||
container->setColor(CONTAINER_TAG_GC_BLACK);
|
container->setColorUnlessGreen(CONTAINER_TAG_GC_BLACK);
|
||||||
// Note, that once object is frozen, it could be concurrently accessed, so
|
// Note, that once object is frozen, it could be concurrently accessed, so
|
||||||
// color and similar attributes shall not be used.
|
// color and similar attributes shall not be used.
|
||||||
container->freeze();
|
container->freeze();
|
||||||
|
|||||||
@@ -30,11 +30,12 @@ typedef enum {
|
|||||||
CONTAINER_TAG_FROZEN = 1 | 1, // shareable
|
CONTAINER_TAG_FROZEN = 1 | 1, // shareable
|
||||||
// Stack container, no need to free, children cleanup still shall be there.
|
// Stack container, no need to free, children cleanup still shall be there.
|
||||||
CONTAINER_TAG_STACK = 2,
|
CONTAINER_TAG_STACK = 2,
|
||||||
// Those container tags shall not be refcounted.
|
|
||||||
// Permanent container, cannot refer to non-permanent containers, so no need to cleanup those.
|
|
||||||
CONTAINER_TAG_PERMANENT = 3 | 1, // shareable
|
|
||||||
// Atomic container, reference counter is atomically updated.
|
// Atomic container, reference counter is atomically updated.
|
||||||
CONTAINER_TAG_ATOMIC = 5 | 1, // shareable
|
CONTAINER_TAG_ATOMIC = 5 | 1, // shareable
|
||||||
|
// Those container tags shall not be refcounted.
|
||||||
|
// Permanent container, cannot refer to non-permanent containers, so no need to cleanup those.
|
||||||
|
// Please check isFreeable() if changing the numeric value.
|
||||||
|
CONTAINER_TAG_PERMANENT = 7 | 1, // shareable
|
||||||
// Shift to get actual counter.
|
// Shift to get actual counter.
|
||||||
CONTAINER_TAG_SHIFT = 3,
|
CONTAINER_TAG_SHIFT = 3,
|
||||||
// Actual value to increment/decrement container by. Tag is in lower bits.
|
// Actual value to increment/decrement container by. Tag is in lower bits.
|
||||||
@@ -44,19 +45,31 @@ typedef enum {
|
|||||||
|
|
||||||
// Those bit masks are applied to objectCount_ field.
|
// Those bit masks are applied to objectCount_ field.
|
||||||
// Shift to get actual object count.
|
// Shift to get actual object count.
|
||||||
CONTAINER_TAG_GC_SHIFT = 5,
|
CONTAINER_TAG_GC_SHIFT = 6,
|
||||||
CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT,
|
CONTAINER_TAG_GC_INCREMENT = 1 << CONTAINER_TAG_GC_SHIFT,
|
||||||
// Color mask of a container.
|
// Color mask of a container.
|
||||||
CONTAINER_TAG_GC_COLOR_MASK = (1 << 2) - 1,
|
CONTAINER_TAG_COLOR_SHIFT = 3,
|
||||||
|
CONTAINER_TAG_GC_COLOR_MASK = (1 << CONTAINER_TAG_COLOR_SHIFT) - 1,
|
||||||
// Colors.
|
// Colors.
|
||||||
|
// In use or free.
|
||||||
CONTAINER_TAG_GC_BLACK = 0,
|
CONTAINER_TAG_GC_BLACK = 0,
|
||||||
|
// Possible member of garbage cycle.
|
||||||
CONTAINER_TAG_GC_GRAY = 1,
|
CONTAINER_TAG_GC_GRAY = 1,
|
||||||
|
// Member of garbage cycle.
|
||||||
CONTAINER_TAG_GC_WHITE = 2,
|
CONTAINER_TAG_GC_WHITE = 2,
|
||||||
|
// Possible root of cycle.
|
||||||
CONTAINER_TAG_GC_PURPLE = 3,
|
CONTAINER_TAG_GC_PURPLE = 3,
|
||||||
|
// Acyclic.
|
||||||
|
CONTAINER_TAG_GC_GREEN = 4,
|
||||||
|
// Orange and red are currently unused.
|
||||||
|
// Candidate cycle awaiting epoch.
|
||||||
|
CONTAINER_TAG_GC_ORANGE = 5,
|
||||||
|
// Candidate cycle awaiting sigma computation.
|
||||||
|
CONTAINER_TAG_GC_RED = 6,
|
||||||
// Individual state bits used during GC and freezing.
|
// Individual state bits used during GC and freezing.
|
||||||
CONTAINER_TAG_GC_MARKED = 1 << 2,
|
CONTAINER_TAG_GC_MARKED = 1 << CONTAINER_TAG_COLOR_SHIFT,
|
||||||
CONTAINER_TAG_GC_BUFFERED = 1 << 3,
|
CONTAINER_TAG_GC_BUFFERED = 1 << (CONTAINER_TAG_COLOR_SHIFT + 1),
|
||||||
CONTAINER_TAG_GC_SEEN = 1 << 4
|
CONTAINER_TAG_GC_SEEN = 1 << (CONTAINER_TAG_COLOR_SHIFT + 2)
|
||||||
} ContainerTag;
|
} ContainerTag;
|
||||||
|
|
||||||
typedef uint32_t container_size_t;
|
typedef uint32_t container_size_t;
|
||||||
@@ -152,10 +165,23 @@ struct ContainerHeader {
|
|||||||
return objectCount_ & CONTAINER_TAG_GC_COLOR_MASK;
|
return objectCount_ & CONTAINER_TAG_GC_COLOR_MASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setColor(unsigned color) {
|
inline void setColorAssertIfGreen(unsigned color) {
|
||||||
|
RuntimeAssert(this->color() != CONTAINER_TAG_GC_GREEN, "Must not be green");
|
||||||
|
setColorEvenIfGreen(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setColorEvenIfGreen(unsigned color) {
|
||||||
|
// TODO: do we need atomic color update?
|
||||||
objectCount_ = (objectCount_ & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
|
objectCount_ = (objectCount_ & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void setColorUnlessGreen(unsigned color) {
|
||||||
|
// TODO: do we need atomic color update?
|
||||||
|
unsigned objectCount_ = objectCount_;
|
||||||
|
if ((objectCount_ & CONTAINER_TAG_GC_COLOR_MASK) != CONTAINER_TAG_GC_GREEN)
|
||||||
|
objectCount_ = (objectCount_ & ~CONTAINER_TAG_GC_COLOR_MASK) | color;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool buffered() const {
|
inline bool buffered() const {
|
||||||
return (objectCount_ & CONTAINER_TAG_GC_BUFFERED) != 0;
|
return (objectCount_ & CONTAINER_TAG_GC_BUFFERED) != 0;
|
||||||
}
|
}
|
||||||
@@ -281,6 +307,8 @@ class Container {
|
|||||||
// Take into account typeInfo's immutability for ARC strategy.
|
// Take into account typeInfo's immutability for ARC strategy.
|
||||||
if ((type_info->flags_ & TF_IMMUTABLE) != 0)
|
if ((type_info->flags_ & TF_IMMUTABLE) != 0)
|
||||||
header_->refCount_ |= CONTAINER_TAG_FROZEN;
|
header_->refCount_ |= CONTAINER_TAG_FROZEN;
|
||||||
|
if ((type_info->flags_ & TF_ACYCLIC) != 0)
|
||||||
|
header_->setColorEvenIfGreen(CONTAINER_TAG_GC_GREEN);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -45,20 +45,21 @@ struct FieldTableRecord {
|
|||||||
// Type for runtime representation of Konan object.
|
// Type for runtime representation of Konan object.
|
||||||
// Keep in sync with runtimeTypeMap in RTTIGenerator.
|
// Keep in sync with runtimeTypeMap in RTTIGenerator.
|
||||||
enum Konan_RuntimeType {
|
enum Konan_RuntimeType {
|
||||||
RT_INVALID = 0,
|
RT_INVALID = 0,
|
||||||
RT_OBJECT = 1,
|
RT_OBJECT = 1,
|
||||||
RT_INT8 = 2,
|
RT_INT8 = 2,
|
||||||
RT_INT16 = 3,
|
RT_INT16 = 3,
|
||||||
RT_INT32 = 4,
|
RT_INT32 = 4,
|
||||||
RT_INT64 = 5,
|
RT_INT64 = 5,
|
||||||
RT_FLOAT32 = 6,
|
RT_FLOAT32 = 6,
|
||||||
RT_FLOAT64 = 7,
|
RT_FLOAT64 = 7,
|
||||||
RT_NATIVE_PTR = 8,
|
RT_NATIVE_PTR = 8,
|
||||||
RT_BOOLEAN = 9
|
RT_BOOLEAN = 9
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Konan_TypeFlags {
|
enum Konan_TypeFlags {
|
||||||
TF_IMMUTABLE = 1 << 0
|
TF_IMMUTABLE = 1 << 0,
|
||||||
|
TF_ACYCLIC = 1 << 1
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Konan_MetaFlags {
|
enum Konan_MetaFlags {
|
||||||
|
|||||||
Reference in New Issue
Block a user