[K/N] Specialize marking call for objects ^KT-54163

Merge-request: KT-MR-7232
Merged-by: Alexander Shabalin <Alexander.Shabalin@jetbrains.com>
This commit is contained in:
Alexander Shabalin
2022-10-04 09:51:08 +00:00
committed by Space Team
parent 377082ee43
commit 98251f42f2
19 changed files with 280 additions and 112 deletions
@@ -522,6 +522,11 @@ internal class Llvm(val context: Context, val module: LLVMModuleRef) : RuntimeAw
val Kotlin_mm_safePointFunctionPrologue by lazyRtFunction
val Kotlin_mm_safePointWhileLoopBody by lazyRtFunction
val Kotlin_processObjectInMark by lazyRtFunction
val Kotlin_processArrayInMark by lazyRtFunction
val Kotlin_processFieldInMark by lazyRtFunction
val Kotlin_processEmptyObjectInMark by lazyRtFunction
val tlsMode by lazy {
when (target) {
KonanTarget.WASM32,
@@ -98,7 +98,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
flags: Int,
classId: Int,
writableTypeInfo: ConstPointer?,
associatedObjects: ConstPointer?) :
associatedObjects: ConstPointer?,
processObjectInMark: ConstPointer?) :
Struct(
runtime.typeInfoType,
@@ -134,7 +135,9 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
*listOfNotNull(writableTypeInfo).toTypedArray(),
associatedObjects
associatedObjects,
processObjectInMark,
)
private fun kotlinStringLiteral(string: String?): ConstPointer = if (string == null) {
@@ -255,7 +258,11 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
flagsFromClass(irClass) or reflectionInfo.reflectionFlags,
context.getLayoutBuilder(irClass).classId,
llvmDeclarations.writableTypeInfoGlobal?.pointer,
associatedObjects = genAssociatedObjects(irClass)
associatedObjects = genAssociatedObjects(irClass),
processObjectInMark = when {
irClass.symbol == context.ir.symbols.array -> constPointer(context.llvm.Kotlin_processArrayInMark.llvmValue)
else -> genProcessObjectInMark(bodyType)
}
)
val typeInfoGlobalValue = if (!irClass.typeInfoHasVtableAttached) {
@@ -271,14 +278,17 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
exportTypeInfoIfRequired(irClass, irClass.llvmTypeInfoPtr)
}
private fun getObjOffsets(bodyType: LLVMTypeRef): List<Int32> =
private fun getIndicesOfObjectFields(bodyType: LLVMTypeRef) : List<Int> =
getStructElements(bodyType).mapIndexedNotNull { index, type ->
if (isObjectType(type)) {
LLVMOffsetOfElement(llvmTargetData, bodyType, index)
} else {
null
index.takeIf {
isObjectType(type)
}
}.map { Int32(it.toInt()) }
}
private fun getObjOffsets(bodyType: LLVMTypeRef): List<Int32> =
getIndicesOfObjectFields(bodyType).map { index ->
Int32(LLVMOffsetOfElement(llvmTargetData, bodyType, index).toInt())
}
fun vtable(irClass: IrClass): ConstArray {
// TODO: compile-time resolution limits binary compatibility.
@@ -473,6 +483,20 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
)
}
private fun genProcessObjectInMark(classType: LLVMTypeRef) : ConstPointer {
val indicesOfObjectFields = getIndicesOfObjectFields(classType)
return when {
indicesOfObjectFields.isEmpty() -> {
// TODO: Try to generate it here instead of importing from the runtime.
constPointer(context.llvm.Kotlin_processEmptyObjectInMark.llvmValue)
}
else -> {
// TODO: specialize for "small" objects
constPointer(context.llvm.Kotlin_processObjectInMark.llvmValue)
}
}
}
// TODO: extract more code common with generate().
fun generateSyntheticInterfaceImpl(
irClass: IrClass,
@@ -545,7 +569,8 @@ internal class RTTIGenerator(override val context: Context) : ContextUtils {
flags = flagsFromClass(irClass) or (if (immutable) TF_IMMUTABLE else 0),
classId = typeHierarchyInfo.classIdLo,
writableTypeInfo = writableTypeInfo,
associatedObjects = null
associatedObjects = null,
processObjectInMark = genProcessObjectInMark(bodyType),
), vtable)
typeInfoWithVtableGlobal.setInitializer(typeInfoWithVtable)
@@ -76,6 +76,11 @@ touchFunction(Kotlin_mm_switchThreadStateRunnable)
touchFunction(Kotlin_mm_safePointFunctionPrologue)
touchFunction(Kotlin_mm_safePointWhileLoopBody)
touchFunction(Kotlin_processObjectInMark)
touchFunction(Kotlin_processArrayInMark)
touchFunction(Kotlin_processFieldInMark)
touchFunction(Kotlin_processEmptyObjectInMark)
#ifdef __cplusplus
} // extern "C"
#endif
@@ -28,31 +28,6 @@ namespace {
[[clang::no_destroy]] std::condition_variable markingCondVar;
[[clang::no_destroy]] std::atomic<bool> markingRequested = false;
struct MarkTraits {
using MarkQueue = gc::ConcurrentMarkAndSweep::MarkQueue;
static bool isEmpty(const MarkQueue& queue) noexcept {
return queue.empty();
}
static void clear(MarkQueue& queue) noexcept {
queue.clear();
}
static ObjHeader* dequeue(MarkQueue& queue) noexcept {
auto& top = queue.front();
queue.pop_front();
auto node = mm::ObjectFactory<gc::ConcurrentMarkAndSweep>::NodeRef::From(top);
return node->GetObjHeader();
}
static void enqueue(MarkQueue& queue, ObjHeader* object) noexcept {
auto& objectData = mm::ObjectFactory<gc::ConcurrentMarkAndSweep>::NodeRef::From(object).ObjectData();
if (!objectData.atomicSetToBlack()) return;
queue.push_front(objectData);
}
};
struct SweepTraits {
using ObjectFactory = mm::ObjectFactory<gc::ConcurrentMarkAndSweep>;
using ExtraObjectsFactory = mm::ExtraObjectDataFactory;
@@ -106,8 +81,8 @@ NO_EXTERNAL_CALLS_CHECK void gc::ConcurrentMarkAndSweep::ThreadData::OnSuspendFo
lock.unlock();
RuntimeLogDebug({kTagGC}, "Parallel marking in thread %d", konan::currentThreadId());
MarkQueue markQueue;
gc::collectRootSetForThread<MarkTraits>(markQueue, threadData_);
MarkStats stats = gc::Mark<MarkTraits>(markQueue);
gc::collectRootSetForThread<internal::MarkTraits>(markQueue, threadData_);
MarkStats stats = gc::Mark<internal::MarkTraits>(markQueue);
gc_.MergeMarkStats(stats);
}
@@ -185,7 +160,7 @@ bool gc::ConcurrentMarkAndSweep::PerformFullGC(int64_t epoch) noexcept {
// Can be unsafe, because we've stopped the world.
auto objectsCountBefore = objectFactory_.GetSizeUnsafe();
auto markStats = gc::Mark<MarkTraits>(markQueue_);
auto markStats = gc::Mark<internal::MarkTraits>(markQueue_);
MergeMarkStats(markStats);
RuntimeLogDebug({kTagGC}, "Waiting for marking in threads");
@@ -272,7 +247,8 @@ void gc::ConcurrentMarkAndSweep::WaitForThreadsReadyToMark() noexcept {
NO_EXTERNAL_CALLS_CHECK void gc::ConcurrentMarkAndSweep::CollectRootSetAndStartMarking() noexcept {
std::unique_lock lock(markingMutex);
markingRequested = false;
gc::collectRootSet<MarkTraits>(markQueue_, [](mm::ThreadData& thread) { return !thread.gc().impl().gc().marking_.load(); });
gc::collectRootSet<internal::MarkTraits>(
markQueue_, [](mm::ThreadData& thread) { return !thread.gc().impl().gc().marking_.load(); });
RuntimeLogDebug({kTagGC}, "Requesting marking in threads");
markingCondVar.notify_all();
}
@@ -280,4 +256,4 @@ NO_EXTERNAL_CALLS_CHECK void gc::ConcurrentMarkAndSweep::CollectRootSetAndStartM
void gc::ConcurrentMarkAndSweep::MergeMarkStats(gc::MarkStats stats) noexcept {
std::unique_lock lock(markingMutex);
lastGCMarkStats_.merge(stats);
}
}
@@ -136,5 +136,34 @@ private:
MarkingBehavior markingBehavior_;
};
namespace internal {
struct MarkTraits {
using MarkQueue = gc::ConcurrentMarkAndSweep::MarkQueue;
static bool isEmpty(const MarkQueue& queue) noexcept { return queue.empty(); }
static void clear(MarkQueue& queue) noexcept { queue.clear(); }
static ObjHeader* dequeue(MarkQueue& queue) noexcept {
auto& top = queue.front();
queue.pop_front();
auto node = mm::ObjectFactory<gc::ConcurrentMarkAndSweep>::NodeRef::From(top);
return node->GetObjHeader();
}
static void enqueue(MarkQueue& queue, ObjHeader* object) noexcept {
auto& objectData = mm::ObjectFactory<gc::ConcurrentMarkAndSweep>::NodeRef::From(object).ObjectData();
if (!objectData.atomicSetToBlack()) return;
queue.push_front(objectData);
}
static void processInMark(MarkQueue& markQueue, ObjHeader* object) noexcept {
auto process = object->type_info()->processObjectInMark;
RuntimeAssert(process != nullptr, "Got null processObjectInMark for object %p", object);
process(static_cast<void*>(&markQueue), object);
}
};
} // namespace internal
} // namespace gc
} // namespace kotlin
@@ -6,6 +6,7 @@
#include "GCImpl.hpp"
#include "GC.hpp"
#include "MarkAndSweepUtils.hpp"
#include "ThreadSuspension.hpp"
#include "std_support/Memory.hpp"
@@ -93,3 +94,18 @@ void gc::GC::StopFinalizerThreadIfRunning() noexcept {
bool gc::GC::FinalizersThreadIsRunning() noexcept {
return impl_->gc().FinalizersThreadIsRunning();
}
// static
ALWAYS_INLINE void gc::GC::processObjectInMark(void* state, ObjHeader* object) noexcept {
gc::internal::processObjectInMark<gc::internal::MarkTraits>(state, object);
}
// static
ALWAYS_INLINE void gc::GC::processArrayInMark(void* state, ArrayHeader* array) noexcept {
gc::internal::processArrayInMark<gc::internal::MarkTraits>(state, array);
}
// static
ALWAYS_INLINE void gc::GC::processFieldInMark(void* state, ObjHeader* field) noexcept {
gc::internal::processFieldInMark<gc::internal::MarkTraits>(state, field);
}
@@ -66,6 +66,10 @@ public:
void StopFinalizerThreadIfRunning() noexcept;
bool FinalizersThreadIsRunning() noexcept;
static void processObjectInMark(void* state, ObjHeader* object) noexcept;
static void processArrayInMark(void* state, ArrayHeader* array) noexcept;
static void processFieldInMark(void* state, ObjHeader* field) noexcept;
private:
std_support::unique_ptr<Impl> impl_;
};
@@ -22,6 +22,56 @@
namespace kotlin {
namespace gc {
namespace internal {
template <typename Traits>
void processFieldInMark(void* state, ObjHeader* field) noexcept {
auto& markQueue = *static_cast<typename Traits::MarkQueue*>(state);
if (field->heap()) {
Traits::enqueue(markQueue, field);
}
}
template <typename Traits>
void processObjectInMark(void* state, ObjHeader* object) noexcept {
auto* typeInfo = object->type_info();
RuntimeAssert(typeInfo != theArrayTypeInfo, "Must not be an array of objects");
for (int i = 0; i < typeInfo->objOffsetsCount_; ++i) {
auto* field = *reinterpret_cast<ObjHeader**>(reinterpret_cast<uintptr_t>(object) + typeInfo->objOffsets_[i]);
if (!field) continue;
processFieldInMark<Traits>(state, field);
}
}
template <typename Traits>
void processArrayInMark(void* state, ArrayHeader* array) noexcept {
RuntimeAssert(array->type_info() == theArrayTypeInfo, "Must be an array of objects");
auto* begin = ArrayAddressOfElementAt(array, 0);
auto* end = ArrayAddressOfElementAt(array, array->count_);
for (auto* it = begin; it != end; ++it) {
auto* field = *it;
if (!field) continue;
processFieldInMark<Traits>(state, field);
}
}
template <typename Traits>
bool collectRoot(typename Traits::MarkQueue& markQueue, ObjHeader* object) noexcept {
if (isNullOrMarker(object))
return false;
if (object->heap()) {
Traits::enqueue(markQueue, object);
} else {
// Each permanent and stack object has own entry in the root set, so it's okay to only process objects in heap.
Traits::processInMark(markQueue, object);
RuntimeAssert(!object->has_meta_object(), "Non-heap object %p may not have an extra object data", object);
}
return true;
}
} // namespace internal
struct MarkStats {
// How many objects are alive.
size_t aliveHeapSet = 0;
@@ -51,11 +101,7 @@ MarkStats Mark(typename Traits::MarkQueue& markQueue) noexcept {
stats.aliveHeapSet++;
stats.aliveHeapSetBytes += mm::GetAllocatedHeapSize(top);
traverseReferredObjects(top, [&](ObjHeader* field) noexcept {
if (field->heap()) {
Traits::enqueue(markQueue, field);
}
});
Traits::processInMark(markQueue, top);
if (auto* extraObjectData = mm::ExtraObjectData::Get(top)) {
if (auto weakCounter = extraObjectData->GetWeakReferenceCounter()) {
@@ -124,20 +170,9 @@ void collectRootSetForThread(typename Traits::MarkQueue& markQueue, mm::ThreadDa
thread.gc().OnStoppedForGC();
size_t stack = 0;
size_t tls = 0;
// TODO: Remove useless mm::ThreadRootSet abstraction.
for (auto value : mm::ThreadRootSet(thread)) {
auto* object = value.object;
if (!isNullOrMarker(object)) {
if (object->heap()) {
Traits::enqueue(markQueue, object);
} else {
traverseReferredObjects(object, [&](ObjHeader* field) noexcept {
// Each permanent and stack object has own entry in the root set.
if (field->heap()) {
Traits::enqueue(markQueue, field);
}
});
RuntimeAssert(!object->has_meta_object(), "Non-heap object %p may not have an extra object data", object);
}
if (internal::collectRoot<Traits>(markQueue, value.object)) {
switch (value.source) {
case mm::ThreadRootSet::Source::kStack:
++stack;
@@ -156,20 +191,9 @@ void collectRootSetGlobals(typename Traits::MarkQueue& markQueue) noexcept {
mm::StableRefRegistry::Instance().ProcessDeletions();
size_t global = 0;
size_t stableRef = 0;
// TODO: Remove useless mm::GlobalRootSet abstraction.
for (auto value : mm::GlobalRootSet()) {
auto* object = value.object;
if (!isNullOrMarker(object)) {
if (object->heap()) {
Traits::enqueue(markQueue, object);
} else {
traverseReferredObjects(object, [&](ObjHeader* field) noexcept {
// Each permanent and stack object has own entry in the root set.
if (field->heap()) {
Traits::enqueue(markQueue, field);
}
});
RuntimeAssert(!object->has_meta_object(), "Non-heap object %p may not have an extra object data", object);
}
if (internal::collectRoot<Traits>(markQueue, value.object)) {
switch (value.source) {
case mm::GlobalRootSet::Source::kGlobal:
++global;
@@ -145,6 +145,14 @@ public:
}
}
static void processInMark(MarkQueue& markQueue, ObjHeader* object) noexcept {
if (object->type_info() == theArrayTypeInfo) {
gc::internal::processArrayInMark<ScopedMarkTraits>(static_cast<void*>(&markQueue), object->array());
} else {
gc::internal::processObjectInMark<ScopedMarkTraits>(static_cast<void*>(&markQueue), object);
}
}
private:
static ScopedMarkTraits* instance_;
@@ -76,3 +76,12 @@ void gc::GC::StopFinalizerThreadIfRunning() noexcept {}
bool gc::GC::FinalizersThreadIsRunning() noexcept {
return false;
}
// static
ALWAYS_INLINE void gc::GC::processObjectInMark(void* state, ObjHeader* object) noexcept {}
// static
ALWAYS_INLINE void gc::GC::processArrayInMark(void* state, ArrayHeader* array) noexcept {}
// static
ALWAYS_INLINE void gc::GC::processFieldInMark(void* state, ObjHeader* field) noexcept {}
@@ -6,6 +6,7 @@
#include "GCImpl.hpp"
#include "GC.hpp"
#include "MarkAndSweepUtils.hpp"
#include "std_support/Memory.hpp"
using namespace kotlin;
@@ -88,3 +89,18 @@ void gc::GC::StopFinalizerThreadIfRunning() noexcept {}
bool gc::GC::FinalizersThreadIsRunning() noexcept {
return false;
}
// static
ALWAYS_INLINE void gc::GC::processObjectInMark(void* state, ObjHeader* object) noexcept {
gc::internal::processObjectInMark<gc::internal::MarkTraits>(state, object);
}
// static
ALWAYS_INLINE void gc::GC::processArrayInMark(void* state, ArrayHeader* array) noexcept {
gc::internal::processArrayInMark<gc::internal::MarkTraits>(state, array);
}
// static
ALWAYS_INLINE void gc::GC::processFieldInMark(void* state, ObjHeader* field) noexcept {
gc::internal::processFieldInMark<gc::internal::MarkTraits>(state, field);
}
@@ -22,32 +22,6 @@ using namespace kotlin;
namespace {
struct MarkTraits {
using MarkQueue = gc::SameThreadMarkAndSweep::MarkQueue;
static bool isEmpty(const MarkQueue& queue) noexcept {
return queue.empty();
}
static void clear(MarkQueue& queue) noexcept {
queue.clear();
}
static ObjHeader* dequeue(MarkQueue& queue) noexcept {
auto& top = queue.front();
queue.pop_front();
auto node = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(top);
return node->GetObjHeader();
}
static void enqueue(MarkQueue& queue, ObjHeader* object) noexcept {
auto& objectData = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(object).ObjectData();
if (objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack) return;
objectData.setColor(gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack);
queue.push_front(objectData);
}
};
struct SweepTraits {
using ObjectFactory = mm::ObjectFactory<gc::SameThreadMarkAndSweep>;
using ExtraObjectsFactory = mm::ExtraObjectDataFactory;
@@ -154,7 +128,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
RuntimeLogInfo(
{kTagGC}, "Started GC epoch %zu. Time since last GC %" PRIu64 " microseconds", epoch_, timeStartUs - lastGCTimestampUs_);
gc::collectRootSet<MarkTraits>(markQueue_, [] (mm::ThreadData&) { return true; });
gc::collectRootSet<internal::MarkTraits>(markQueue_, [](mm::ThreadData&) { return true; });
auto timeRootSetUs = konan::getTimeMicros();
// Can be unsafe, because we've stopped the world.
auto objectsCountBefore = objectFactory_.GetSizeUnsafe();
@@ -162,7 +136,7 @@ bool gc::SameThreadMarkAndSweep::PerformFullGC() noexcept {
RuntimeLogInfo(
{kTagGC}, "Collected root set of size %zu in %" PRIu64 " microseconds", markQueue_.size(),
timeRootSetUs - timeSuspendUs);
auto markStats = gc::Mark<MarkTraits>(markQueue_);
auto markStats = gc::Mark<internal::MarkTraits>(markQueue_);
auto timeMarkUs = konan::getTimeMicros();
RuntimeLogDebug({kTagGC}, "Marked %zu objects in %" PRIu64 " microseconds", markStats.aliveHeapSet, timeMarkUs - timeRootSetUs);
scheduler.gcData().UpdateAliveSetBytes(markStats.aliveHeapSetBytes);
@@ -110,6 +110,34 @@ private:
namespace internal {
struct MarkTraits {
using MarkQueue = gc::SameThreadMarkAndSweep::MarkQueue;
static bool isEmpty(const MarkQueue& queue) noexcept { return queue.empty(); }
static void clear(MarkQueue& queue) noexcept { queue.clear(); }
static ObjHeader* dequeue(MarkQueue& queue) noexcept {
auto& top = queue.front();
queue.pop_front();
auto node = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(top);
return node->GetObjHeader();
}
static void enqueue(MarkQueue& queue, ObjHeader* object) noexcept {
auto& objectData = mm::ObjectFactory<gc::SameThreadMarkAndSweep>::NodeRef::From(object).ObjectData();
if (objectData.color() == gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack) return;
objectData.setColor(gc::SameThreadMarkAndSweep::ObjectData::Color::kBlack);
queue.push_front(objectData);
}
static void processInMark(MarkQueue& markQueue, ObjHeader* object) noexcept {
auto process = object->type_info()->processObjectInMark;
RuntimeAssert(process != nullptr, "Got null processObjectInMark for object %p", object);
process(static_cast<void*>(&markQueue), object);
}
};
SameThreadMarkAndSweep::SafepointFlag loadSafepointFlag() noexcept;
} // namespace internal
@@ -3796,6 +3796,22 @@ CODEGEN_INLINE_POLICY RUNTIME_NOTHROW void Kotlin_mm_safePointWhileLoopBody() {
// no-op, used by the new MM only.
}
RUNTIME_NOTHROW ALWAYS_INLINE void Kotlin_processObjectInMark(void* state, ObjHeader* object) {
// no-op, used by the new MM only.
}
RUNTIME_NOTHROW ALWAYS_INLINE void Kotlin_processArrayInMark(void* state, ObjHeader* object) {
// no-op, used by the new MM only.
}
RUNTIME_NOTHROW ALWAYS_INLINE void Kotlin_processFieldInMark(void* state, ObjHeader* field) {
// no-op, used by the new MM only.
}
RUNTIME_NOTHROW ALWAYS_INLINE void Kotlin_processEmptyObjectInMark(void* state, ObjHeader* object) {
// no-op, used by the new MM only.
}
} // extern "C"
#if !KONAN_NO_EXCEPTIONS
@@ -561,4 +561,9 @@ bool FinalizersThreadIsRunning() noexcept;
} // namespace kotlin
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processObjectInMark(void* state, ObjHeader* object);
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processArrayInMark(void* state, ObjHeader* object);
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processFieldInMark(void* state, ObjHeader* field);
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processEmptyObjectInMark(void* state, ObjHeader* object);
#endif // RUNTIME_MEMORY_H
@@ -661,10 +661,12 @@ static const TypeInfo* createTypeInfo(
if ((superType->flags_ & TF_IMMUTABLE) != 0) {
result->flags_ |= TF_IMMUTABLE;
}
result->processObjectInMark = superType->processObjectInMark;
} else {
result->instanceSize_ = fieldsInfo->instanceSize_;
result->objOffsets_ = fieldsInfo->objOffsets_;
result->objOffsetsCount_ = fieldsInfo->objOffsetsCount_;
result->processObjectInMark = fieldsInfo->processObjectInMark;
}
result->classId_ = superType->classId_;
@@ -28,8 +28,10 @@ private:
int32_t instanceSize_ = 0;
std_support::vector<int32_t> objOffsets_;
int32_t objOffsetsCount_ = 0;
int32_t flags_ = 0;
const TypeInfo* superType_ = nullptr;
void (*processObjectInMark_)(void*, ObjHeader*) = nullptr;
};
public:
@@ -57,7 +59,16 @@ public:
template <typename Payload>
class ArrayBuilder : public Builder {
public:
ArrayBuilder() noexcept { instanceSize_ = -static_cast<int32_t>(sizeof(Payload)); }
ArrayBuilder() noexcept {
instanceSize_ = -static_cast<int32_t>(sizeof(Payload));
if constexpr (std::is_same_v<Payload, ObjHeader*>) {
// Following RTTIGenerator.kt
objOffsetsCount_ = 1;
processObjectInMark_ = Kotlin_processArrayInMark;
} else {
processObjectInMark_ = Kotlin_processEmptyObjectInMark;
}
}
ArrayBuilder&& addFlag(Konan_TypeFlags flag) noexcept {
flags_ |= flag;
@@ -75,12 +86,8 @@ public:
typeInfo_.instanceSize_ = builder.instanceSize_;
objOffsets_ = std::move(builder.objOffsets_);
typeInfo_.objOffsets_ = objOffsets_.data();
if (&typeInfo_ == theArrayTypeInfo) {
// Following RTTIGenerator.kt
typeInfo_.objOffsetsCount_ = 1;
} else {
typeInfo_.objOffsetsCount_ = objOffsets_.size();
}
typeInfo_.objOffsetsCount_ = builder.objOffsetsCount_;
typeInfo_.processObjectInMark = builder.processObjectInMark_;
typeInfo_.flags_ = builder.flags_;
typeInfo_.superType_ = builder.superType_;
}
@@ -174,6 +181,8 @@ TypeInfoHolder::ObjectBuilder<Payload>::ObjectBuilder() noexcept {
auto& actualField = payload.*field;
objOffsets_.push_back(reinterpret_cast<uintptr_t>(&actualField) - reinterpret_cast<uintptr_t>(object.header()));
}
objOffsetsCount_ = objOffsets_.size();
processObjectInMark_ = Kotlin_processObjectInMark;
}
namespace internal {
@@ -132,16 +132,16 @@ struct TypeInfo {
// Null-terminated array.
const AssociatedObjectTableRecord* associatedObjects;
// Invoked on an object during mark phase.
// TODO: Consider providing a generic traverse method instead.
void (*processObjectInMark)(void* state, ObjHeader* object);
// vtable starts just after declared contents of the TypeInfo:
// void* const vtable_[];
#ifdef __cplusplus
inline VTableElement const* vtable() const {
return reinterpret_cast<VTableElement const*>(this + 1);
}
inline VTableElement const* vtable() const { return reinterpret_cast<VTableElement const*>(this + 1); }
inline VTableElement* vtable() {
return reinterpret_cast<VTableElement*>(this + 1);
}
inline VTableElement* vtable() { return reinterpret_cast<VTableElement*>(this + 1); }
inline bool IsArray() const { return instanceSize_ < 0; }
@@ -632,3 +632,20 @@ void kotlin::StartFinalizerThreadIfNeeded() noexcept {
bool kotlin::FinalizersThreadIsRunning() noexcept {
return mm::GlobalData::Instance().gc().FinalizersThreadIsRunning();
}
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processObjectInMark(void* state, ObjHeader* object) {
gc::GC::processObjectInMark(state, object);
}
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processArrayInMark(void* state, ObjHeader* object) {
gc::GC::processArrayInMark(state, object->array());
}
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processFieldInMark(void* state, ObjHeader* field) {
gc::GC::processFieldInMark(state, field);
}
RUNTIME_NOTHROW ALWAYS_INLINE extern "C" void Kotlin_processEmptyObjectInMark(void* state, ObjHeader* object) {
// Empty object. Nothing to do.
// TODO: Try to generate it in the code generator.
}