[K/N] Decouple ObjectFactory from concrete ObjectData ^KT-60928

* Use type_layout to declaratively express heap object headers in both
  custom allocator and ObjectFactory.
* Invoke constructor (w/o invoking Kotin constructors) for created
  objects and arrays from both custom allocator and ObjectFactory.
  Previously:
  - custom allocator only checked body for nullability (now this is
    performed in body constructor)
  - ObjectFactory only constructed ObjectData
* In each GC have a AllocatorImpl.hpp and ObjectData.hpp headers
  the first encapsulating allocator-specific types, the second
  containing specific ObjectData implementation.
* In each GC have a separate ObjectFactoryTraits that does not
  actually depend on the specific GC anymore.
* Each GC now expose ObjectData (as undefined type) and its descriptor,
  the latter being used by the custom allocator and ObjectFactory.
* Descriptors for ObjectBody and ArrayBody now live in Memory.h and the
  code calculating size is now shared. Their constructors check that the
  memory is zeroed (Kotlin constructors will expect this).
This commit is contained in:
Alexander Shabalin
2023-07-28 15:08:15 +02:00
committed by Space Team
parent d956a5504d
commit 83a70ddf8b
37 changed files with 901 additions and 673 deletions
@@ -19,9 +19,11 @@
#include <utility>
#include "Alignment.hpp"
#include "KAssert.h"
#include "Common.h"
#include "TypeInfo.h"
#include "TypeLayout.hpp"
#include "Atomic.h"
#include "PointerBits.h"
#include "Utils.hpp"
@@ -125,6 +127,7 @@ struct ObjHeader {
static MetaObjHeader* createMetaObject(ObjHeader* object);
static void destroyMetaObject(ObjHeader* object);
};
static_assert(alignof(ObjHeader) <= kotlin::kObjectAlignment);
// Header of value type array objects. Keep layout in sync with that of object header.
struct ArrayHeader {
@@ -140,6 +143,61 @@ struct ArrayHeader {
// Elements count. Element size is stored in instanceSize_ field of TypeInfo, negated.
uint32_t count_;
};
static_assert(alignof(ArrayHeader) <= kotlin::kObjectAlignment);
#ifndef KONAN_WASM
namespace kotlin {
struct ObjectBody;
struct ArrayBody;
template <>
struct type_layout::descriptor<ObjectBody> {
class type {
public:
using value_type = ObjectBody;
explicit type(const TypeInfo* typeInfo) noexcept : size_(typeInfo->instanceSize_ - sizeof(ObjHeader)) {}
static constexpr size_t alignment() noexcept { return kObjectAlignment; }
uint64_t size() const noexcept { return size_; }
value_type* construct(uint8_t* ptr) noexcept {
RuntimeAssert(isZeroed(std_support::span<uint8_t>(ptr, size_)), "ObjectBodyDescriptor::construct@%p memory is not zeroed", ptr);
return reinterpret_cast<value_type*>(ptr);
}
private:
uint64_t size_;
};
};
template <>
struct type_layout::descriptor<ArrayBody> {
class type {
public:
using value_type = ArrayBody;
explicit type(const TypeInfo* typeInfo, uint32_t count) noexcept :
// -(int32_t min) * uint32_t max cannot overflow uint64_t. And are capped
// at about half of uint64_t max.
size_(static_cast<uint64_t>(-typeInfo->instanceSize_) * count) {}
static constexpr size_t alignment() noexcept { return kObjectAlignment; }
uint64_t size() const noexcept { return size_; }
value_type* construct(uint8_t* ptr) noexcept {
RuntimeAssert(isZeroed(std_support::span<uint8_t>(ptr, size_)), "ArrayBodyDescriptor::construct@%p memory is not zeroed", ptr);
return reinterpret_cast<ArrayBody*>(ptr);
}
private:
uint64_t size_;
};
};
} // namespace kotlin
#endif
ALWAYS_INLINE bool isPermanentOrFrozen(const ObjHeader* obj);
ALWAYS_INLINE bool isShareable(const ObjHeader* obj);
+10 -1
View File
@@ -7,6 +7,9 @@
#include <climits>
#include <cstdint>
#include <cstring>
using namespace kotlin;
namespace {
@@ -68,4 +71,10 @@ struct HashCompineImpl<64> {
size_t kotlin::CombineHash(size_t seed, size_t value) {
return HashCompineImpl<sizeof(std::size_t) * CHAR_BIT>::fn(seed, value);
}
}
bool kotlin::isZeroed(std_support::span<uint8_t> span) noexcept {
if (span.size() == 0) return true;
if (span[0] != 0) return false;
return memcmp(span.data(), span.data() + 1, span.size() - 1) == 0;
}
@@ -10,6 +10,8 @@
#include <type_traits>
#include "std_support/Span.hpp"
namespace kotlin {
// A helper for implementing classes with disabled copy constructor and copy assignment.
@@ -98,6 +100,9 @@ size_t CombineHash(size_t seed, size_t value);
#define ownerOf(type, field, ref) *reinterpret_cast<type*>(reinterpret_cast<char*>(&ref) - offsetof(type, field))
// Returns `true` if the entire `span` is zeroed.
bool isZeroed(std_support::span<uint8_t> span) noexcept;
} // namespace kotlin
#endif // RUNTIME_UTILS_H
@@ -5,6 +5,7 @@
#include "Utils.hpp"
#include <array>
#include <type_traits>
#include "gmock/gmock.h"
@@ -73,3 +74,26 @@ TEST(UtilsTest, OwnerOf) {
EXPECT_THAT(&c, &Container::fromX(c.x()));
EXPECT_THAT(&c, &Container::fromY(c.y()));
}
TEST(UtilsTest, IsZeroed) {
std::array<uint8_t, 0> empty;
EXPECT_TRUE(isZeroed(empty));
std::array<uint8_t, 1> zeroed1 = {0};
EXPECT_TRUE(isZeroed(zeroed1));
std::array<uint8_t, 1> notZeroed1 = {1};
EXPECT_FALSE(isZeroed(notZeroed1));
std::array<uint8_t, 3> zeroed3 = {0, 0, 0};
EXPECT_TRUE(isZeroed(zeroed3));
std::array<uint8_t, 3> notZeroed3_0 = {1, 0, 0};
EXPECT_FALSE(isZeroed(notZeroed3_0));
std::array<uint8_t, 3> notZeroed3_1 = {0, 1, 0};
EXPECT_FALSE(isZeroed(notZeroed3_1));
std::array<uint8_t, 3> notZeroed3_2 = {0, 0, 1};
EXPECT_FALSE(isZeroed(notZeroed3_2));
}