Small MM refactor, ignore acyclic and frozen objects in cycle collector. (#2354)

This commit is contained in:
Nikolay Igotti
2018-11-22 17:38:51 +03:00
committed by GitHub
parent 901219ed5f
commit 70efeb1433
4 changed files with 188 additions and 101 deletions
@@ -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
+97 -79
View File
@@ -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();
+37 -9
View File
@@ -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);
} }
}; };
+11 -10
View File
@@ -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 {