Tracking globals (#4542)

This commit is contained in:
Alexander Shabalin
2020-11-27 09:30:32 +03:00
committed by Stanislav Erokhin
parent 66de5dceb9
commit 2057e5c8f1
17 changed files with 302 additions and 56 deletions
@@ -1092,9 +1092,9 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
val ctor = codegen.llvmFunction(defaultConstructor)
val initFunction =
if (storageKind == ObjectStorageKind.SHARED && context.config.threadsAreAllowed) {
context.llvm.initSharedInstanceFunction
context.llvm.initSingletonFunction
} else {
context.llvm.initInstanceFunction
context.llvm.initThreadLocalSingleton
}
val args = listOf(objectPtr, typeInfo, ctor)
val newValue = call(initFunction, args, Lifetime.GLOBAL, exceptionHandler)
@@ -499,8 +499,9 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
val allocInstanceFunction = importRtFunction("AllocInstance")
val allocArrayFunction = importRtFunction("AllocArrayInstance")
val initInstanceFunction = importRtFunction("InitInstance")
val initSharedInstanceFunction = importRtFunction("InitSharedInstance")
val initThreadLocalSingleton = importRtFunction("InitThreadLocalSingleton")
val initSingletonFunction = importRtFunction("InitSingleton")
val initAndRegisterGlobalFunction = importRtFunction("InitAndRegisterGlobal")
val updateHeapRefFunction = importRtFunction("UpdateHeapRef")
val updateStackRefFunction = importRtFunction("UpdateStackRef")
val updateReturnRefFunction = importRtFunction("UpdateReturnRef")
@@ -412,15 +412,28 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
appendingTo(bbInit) {
context.llvm.fileInitializers
.forEach { irField ->
if (irField.initializer?.expression !is IrConst<*>?) {
if (irField.storageKind != FieldStorageKind.THREAD_LOCAL) {
if (irField.storageKind != FieldStorageKind.THREAD_LOCAL) {
val address = context.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress(
functionGenerationContext
)
val initialValue = if (irField.initializer?.expression !is IrConst<*>?) {
val initialization = evaluateExpression(irField.initializer!!.expression)
val address = context.llvmDeclarations.forStaticField(irField).storageAddressAccess.getAddress(
functionGenerationContext
)
if (irField.storageKind == FieldStorageKind.SHARED_FROZEN)
freeze(initialization, currentCodeContext.exceptionHandler)
storeAny(initialization, address, false)
initialization
} else {
null
}
val needRegistration =
context.memoryModel == MemoryModel.EXPERIMENTAL && // only for the new MM
irField.type.binaryTypeIsReference() && // only for references
(initialValue != null || // which are initialized from heap object
!irField.isFinal) // or are not final
if (needRegistration) {
call(context.llvm.initAndRegisterGlobalFunction, listOf(address, initialValue
?: kNullObjHeaderPtr))
} else if (initialValue != null) {
storeAny(initialValue, address, false)
}
}
}
@@ -2320,7 +2320,7 @@ OBJ_GETTER(allocArrayInstance, const TypeInfo* type_info, int32_t elements) {
}
template <bool Strict>
OBJ_GETTER(initInstance,
OBJ_GETTER(initThreadLocalSingleton,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
ObjHeader* value = *location;
if (value != nullptr) {
@@ -2345,8 +2345,7 @@ OBJ_GETTER(initInstance,
}
template <bool Strict>
OBJ_GETTER(initSharedInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
OBJ_GETTER(initSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
#if KONAN_NO_THREADS
ObjHeader* value = *location;
if (value != nullptr) {
@@ -3328,22 +3327,22 @@ OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* typeInfo, int32_t elements
RETURN_RESULT_OF(allocArrayInstance<false>, typeInfo, elements);
}
OBJ_GETTER(InitInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initInstance<true>, location, typeInfo, ctor);
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initThreadLocalSingleton<true>, location, typeInfo, ctor);
}
OBJ_GETTER(InitInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initInstance<false>, location, typeInfo, ctor);
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initThreadLocalSingleton<false>, location, typeInfo, ctor);
}
OBJ_GETTER(InitSharedInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initSharedInstance<true>, location, typeInfo, ctor);
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initSingleton<true>, location, typeInfo, ctor);
}
OBJ_GETTER(InitSharedInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initSharedInstance<false>, location, typeInfo, ctor);
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(initSingleton<false>, location, typeInfo, ctor);
}
void RUNTIME_NOTHROW InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
RuntimeCheck(false, "Global registration is impossible in legacy MM");
}
RUNTIME_NOTHROW void SetStackRefStrict(ObjHeader** location, const ObjHeader* object) {
@@ -309,15 +309,11 @@ OBJ_GETTER(AllocInstanceRelaxed, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocArrayInstanceStrict, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(AllocArrayInstanceRelaxed, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(InitInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitThreadLocalSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitThreadLocalSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstanceStrict,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstanceRelaxed,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSingletonStrict, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSingletonRelaxed, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
MODEL_VARIANTS(void, SetStackRef, ObjHeader** location, const ObjHeader* object);
MODEL_VARIANTS(void, SetHeapRef, ObjHeader** location, const ObjHeader* object);
@@ -25,8 +25,9 @@ void ensureUsed(Ret (*f)(Args...)) {
void EnsureDeclarationsEmitted() {
ensureUsed(AllocInstance);
ensureUsed(AllocArrayInstance);
ensureUsed(InitInstance);
ensureUsed(InitSharedInstance);
ensureUsed(InitThreadLocalSingleton);
ensureUsed(InitSingleton);
ensureUsed(InitAndRegisterGlobal);
ensureUsed(UpdateHeapRef);
ensureUsed(UpdateStackRef);
ensureUsed(UpdateReturnRef);
+7 -4
View File
@@ -142,11 +142,14 @@ OBJ_GETTER(AllocInstance, const TypeInfo* type_info) RUNTIME_NOTHROW;
OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements);
OBJ_GETTER(InitInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSharedInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*));
// `initialValue` may be `nullptr`, which signifies that the appropriate initial value was already
// set by static initialization.
// TODO: When global initialization becomes lazy, this signature won't do.
void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) RUNTIME_NOTHROW;
//
// Object reference management.
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_MULTI_SOURCE_QUEUE_H
#define RUNTIME_MULTI_SOURCE_QUEUE_H
#include <list>
namespace kotlin {
// A queue that is constructed by collecting subqueues from several `Producer`s.
template <typename T>
class MultiSourceQueue {
public:
class Producer {
public:
void Insert(const T& value) noexcept { queue_.push_back(value); }
private:
friend class MultiSourceQueue;
std::list<T> queue_;
};
using Iterator = typename std::list<T>::iterator;
Iterator begin() noexcept { return commonQueue_.begin(); }
Iterator end() noexcept { return commonQueue_.end(); }
// Merge `producer`s queue with `this`. `producer` will have empty queue after the call.
// This call is performed without heap allocations. TODO: Test that no allocations are happening.
void Collect(Producer* producer) noexcept { commonQueue_.splice(commonQueue_.end(), producer->queue_); }
private:
// Using `std::list` as it allows to implement `Collect` without memory allocations,
// which is important for GC mark phase.
std::list<T> commonQueue_;
};
} // namespace kotlin
#endif // RUNTIME_MULTI_SOURCE_QUEUE_H
@@ -0,0 +1,89 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "MultiSourceQueue.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using namespace kotlin;
using IntQueue = MultiSourceQueue<int>;
TEST(MultiSourceQueueTest, Empty) {
IntQueue queue;
std::vector<int> actual;
for (int element : queue) {
actual.push_back(element);
}
EXPECT_THAT(actual, testing::IsEmpty());
}
TEST(MultiSourceQueueTest, DoNotCollect) {
IntQueue queue;
IntQueue::Producer producer;
producer.Insert(1);
producer.Insert(2);
std::vector<int> actual;
for (int element : queue) {
actual.push_back(element);
}
EXPECT_THAT(actual, testing::IsEmpty());
}
TEST(MultiSourceQueueTest, Collect) {
IntQueue queue;
IntQueue::Producer producer1;
IntQueue::Producer producer2;
producer1.Insert(1);
producer1.Insert(2);
producer2.Insert(10);
producer2.Insert(20);
queue.Collect(&producer1);
queue.Collect(&producer2);
std::vector<int> actual;
for (int element : queue) {
actual.push_back(element);
}
EXPECT_THAT(actual, testing::ElementsAre(1, 2, 10, 20));
}
TEST(MultiSourceQueueTest, CollectSeveralTimes) {
IntQueue queue;
IntQueue::Producer producer;
// Add 2 elements and collect.
producer.Insert(1);
producer.Insert(2);
queue.Collect(&producer);
// Add another element and collect.
producer.Insert(3);
queue.Collect(&producer);
// Collect without adding elements.
queue.Collect(&producer);
// Add yet another two elements and collect.
producer.Insert(4);
producer.Insert(5);
queue.Collect(&producer);
std::vector<int> actual;
for (int element : queue) {
actual.push_back(element);
}
EXPECT_THAT(actual, testing::ElementsAre(1, 2, 3, 4, 5));
}
@@ -6,6 +6,7 @@
#ifndef RUNTIME_MM_GLOBAL_DATA_H
#define RUNTIME_MM_GLOBAL_DATA_H
#include "GlobalsRegistry.hpp"
#include "ThreadRegistry.hpp"
#include "Utils.hpp"
@@ -18,6 +19,7 @@ public:
static GlobalData& Instance() noexcept { return instance_; }
ThreadRegistry& threadRegistry() { return threadRegistry_; }
GlobalsRegistry& globalsRegistry() { return globalsRegistry_; }
private:
GlobalData();
@@ -26,6 +28,7 @@ private:
static GlobalData instance_;
ThreadRegistry threadRegistry_;
GlobalsRegistry globalsRegistry_;
};
} // namespace mm
@@ -0,0 +1,30 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#include "GlobalsRegistry.hpp"
#include <iterator>
#include "GlobalData.hpp"
#include "ThreadData.hpp"
using namespace kotlin;
// static
mm::GlobalsRegistry& mm::GlobalsRegistry::Instance() noexcept {
return GlobalData::Instance().globalsRegistry();
}
void mm::GlobalsRegistry::RegisterStorageForGlobal(mm::ThreadData* threadData, ObjHeader** location) noexcept {
threadData->globalsThreadQueue()->Insert(location);
}
void mm::GlobalsRegistry::ProcessThread(mm::ThreadData* threadData) noexcept {
RuntimeAssert(threadData->isWaitingForGC(), "Thread must be waiting for GC to complete.");
globals_.Collect(threadData->globalsThreadQueue());
}
mm::GlobalsRegistry::GlobalsRegistry() = default;
mm::GlobalsRegistry::~GlobalsRegistry() = default;
@@ -0,0 +1,50 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
#ifndef RUNTIME_MM_GLOBALS_REGISTRY_H
#define RUNTIME_MM_GLOBALS_REGISTRY_H
#include "Memory.h"
#include "MultiSourceQueue.hpp"
#include "ThreadRegistry.hpp"
#include "Utils.hpp"
namespace kotlin {
namespace mm {
class GlobalsRegistry : Pinned {
public:
using ThreadQueue = MultiSourceQueue<ObjHeader**>::Producer;
using Iterator = std::list<ObjHeader**>::iterator;
static GlobalsRegistry& Instance() noexcept;
void RegisterStorageForGlobal(mm::ThreadData* threadData, ObjHeader** location) noexcept;
// Collect globals from thread corresponding to `threadData`. Thread must be waiting for GC.
// Only one thread can call this method.
void ProcessThread(mm::ThreadData* threadData) noexcept;
// These must be called on the same thread as `ProcessThread` to avoid races.
// TODO: Iteration over `globals_` will be slow, because it's `std::list` collected at different times from
// different threads, and so the nodes are all over the memory. Use metrics to understand how
// much of a problem is it.
Iterator begin() noexcept { return globals_.begin(); }
Iterator end() noexcept { return globals_.end(); }
private:
friend class GlobalData;
GlobalsRegistry();
~GlobalsRegistry();
MultiSourceQueue<ObjHeader**> globals_;
};
} // namespace mm
} // namespace kotlin
#endif // RUNTIME_MM_GLOBALS_REGISTRY_H
@@ -5,6 +5,7 @@
#include "Memory.h"
#include "GlobalsRegistry.hpp"
#include "ThreadData.hpp"
#include "ThreadRegistry.hpp"
#include "Utils.hpp"
@@ -39,3 +40,18 @@ extern "C" MemoryState* InitMemory(bool firstRuntime) {
extern "C" void DeinitMemory(MemoryState* state, bool destroyRuntime) {
mm::ThreadRegistry::Instance().Unregister(FromMemoryState(state));
}
extern "C" OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
// TODO: This should only be called if singleton is actually created here. It's possible that the
// singleton will be created on a different thread and here we should check that, instead of creating
// another one (and registering `location` twice).
mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location);
RuntimeCheck(false, "Unimplemented");
}
extern "C" RUNTIME_NOTHROW void InitAndRegisterGlobal(ObjHeader** location, const ObjHeader* initialValue) {
auto* threadData = mm::ThreadRegistry::Instance().CurrentThreadData();
mm::GlobalsRegistry::Instance().RegisterStorageForGlobal(threadData, location);
RuntimeCheck(false, "Unimplemented");
}
+1 -5
View File
@@ -59,11 +59,7 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* type_info, int32_t elements) {
RuntimeCheck(false, "Unimplemented");
}
OBJ_GETTER(InitInstance, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RuntimeCheck(false, "Unimplemented");
}
OBJ_GETTER(InitSharedInstance, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RuntimeCheck(false, "Unimplemented");
}
@@ -8,6 +8,7 @@
#include <pthread.h>
#include "GlobalsRegistry.hpp"
#include "Utils.hpp"
namespace kotlin {
@@ -23,8 +24,16 @@ public:
pthread_t threadId() const noexcept { return threadId_; }
bool isWaitingForGC() const noexcept {
// TODO: Implement.
return false;
}
GlobalsRegistry::ThreadQueue* globalsThreadQueue() noexcept { return &globalsThreadQueue_; }
private:
const pthread_t threadId_;
GlobalsRegistry::ThreadQueue globalsThreadQueue_;
};
} // namespace mm
@@ -19,14 +19,12 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
RETURN_RESULT_OF(AllocArrayInstanceRelaxed, typeInfo, elements);
}
OBJ_GETTER(InitInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitInstanceRelaxed, location, typeInfo, ctor);
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitThreadLocalSingletonRelaxed, location, typeInfo, ctor);
}
OBJ_GETTER(InitSharedInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitSharedInstanceRelaxed, location, typeInfo, ctor);
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitSingletonRelaxed, location, typeInfo, ctor);
}
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {
@@ -19,14 +19,12 @@ OBJ_GETTER(AllocArrayInstance, const TypeInfo* typeInfo, int32_t elements) {
RETURN_RESULT_OF(AllocArrayInstanceStrict, typeInfo, elements);
}
OBJ_GETTER(InitInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitInstanceStrict, location, typeInfo, ctor);
OBJ_GETTER(InitThreadLocalSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitThreadLocalSingletonStrict, location, typeInfo, ctor);
}
OBJ_GETTER(InitSharedInstance,
ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitSharedInstanceStrict, location, typeInfo, ctor);
OBJ_GETTER(InitSingleton, ObjHeader** location, const TypeInfo* typeInfo, void (*ctor)(ObjHeader*)) {
RETURN_RESULT_OF(InitSingletonStrict, location, typeInfo, ctor);
}
RUNTIME_NOTHROW void ReleaseHeapRef(const ObjHeader* object) {