Skip to content

Commit d215ab6

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[vm] Assign top-level classes cids outside 16-bit range
Right now we assign class ids to top-level classes, abstract classes as well as concrete classes. All of them have allocated from a 16-bit pool of ids. The VM FATAL()s once it hits that limit. Customers who run very large programs (significant amount of generated code) on the Dart VM have started to hit this 16-bit class limit. Concrete classes can have instances in the heap. Our current heap layout only allows 16-bit class ids to be encoded in the header word. To avoid increasing the size of heap objects or shrinking the size of the identity hash code to 16-bit we keep class ids in object headers to be 16-bit. Abstract classes cannot have instances in the heap. Though their class ids are encoded in type objects. Furthermore we sort classes in AOT/AppJIT mode to perform fast class-id range checks. To avoid impacting this optimization we treat abstract classes the same way as concrete classes. Top-level classes cannot have instances in the heap. Their class ids are only used in the runtime code, for example for hot-reload as well as part of the service protocol. => We can allocate class ids outside the 16-bit range for top-level classes, thereby freeing a significant amount of space in the 16-bit range. This CL does exactly that: We change classid_t to be int32_t. The ClassLayout::id_ can now be assigned ids outside 16-bit range for top-level classes. To do this we keep dart classes and top level classes as separate arrays in the ClassTable. Issue #42533 See also b/160229360 Change-Id: I6710a644e7b0ab2d4f4c792bef8e1f91cb117421 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/153607 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent 4f66bb0 commit d215ab6

24 files changed

+470
-127
lines changed

runtime/platform/globals.h

+4-2
Original file line numberDiff line numberDiff line change
@@ -479,8 +479,10 @@ const int64_t kSignBitDouble = DART_INT64_C(0x8000000000000000);
479479
typedef intptr_t word;
480480
typedef uintptr_t uword;
481481

482-
// Size of a class id.
483-
typedef uint16_t classid_t;
482+
// Size of a class id assigned to concrete, abstract and top-level classes.
483+
//
484+
// We use a signed integer type here to make it comparable with intptr_t.
485+
typedef int32_t classid_t;
484486

485487
// Byte sizes.
486488
const int kWordSize = sizeof(word);

runtime/vm/class_finalizer.cc

+7-2
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ void ClassFinalizer::FinalizeClass(const Class& cls) {
11701170
RemoveCHAOptimizedCode(cls, cids);
11711171
}
11721172

1173-
if (FLAG_use_cha_deopt) {
1173+
if (FLAG_use_cha_deopt && !cls.IsTopLevel()) {
11741174
Zone* zone = thread->zone();
11751175
ClassTable* class_table = thread->isolate()->class_table();
11761176
auto& interface_class = Class::Handle(zone);
@@ -1482,7 +1482,12 @@ class CidRewriteVisitor : public ObjectVisitor {
14821482
void VisitObject(ObjectPtr obj) {
14831483
if (obj->IsClass()) {
14841484
ClassPtr cls = Class::RawCast(obj);
1485-
cls->ptr()->id_ = Map(cls->ptr()->id_);
1485+
const classid_t old_cid = cls->ptr()->id_;
1486+
if (ClassTable::IsTopLevelCid(old_cid)) {
1487+
// We don't remap cids of top level classes.
1488+
return;
1489+
}
1490+
cls->ptr()->id_ = Map(old_cid);
14861491
} else if (obj->IsField()) {
14871492
FieldPtr field = Field::RawCast(obj);
14881493
field->ptr()->guarded_cid_ = Map(field->ptr()->guarded_cid_);

runtime/vm/class_table.cc

+103-14
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include "vm/class_table.h"
66

7+
#include <limits>
78
#include <memory>
89

910
#include "platform/atomic.h"
@@ -81,7 +82,10 @@ void ClassTable::set_table(ClassPtr* table) {
8182
ClassTable::ClassTable(SharedClassTable* shared_class_table)
8283
: top_(kNumPredefinedCids),
8384
capacity_(0),
84-
table_(NULL),
85+
tlc_top_(0),
86+
tlc_capacity_(0),
87+
table_(nullptr),
88+
tlc_table_(nullptr),
8589
old_class_tables_(new MallocGrowableArray<ClassPtr*>()),
8690
shared_class_table_(shared_class_table) {
8791
if (Dart::vm_isolate() == NULL) {
@@ -122,6 +126,7 @@ ClassTable::~ClassTable() {
122126
delete old_class_tables_;
123127
}
124128
free(table_.load());
129+
free(tlc_table_.load());
125130
}
126131

127132
void ClassTable::AddOldTable(ClassPtr* old_class_table) {
@@ -149,7 +154,8 @@ void SharedClassTable::FreeOldTables() {
149154
void ClassTable::Register(const Class& cls) {
150155
ASSERT(Thread::Current()->IsMutatorThread());
151156

152-
const intptr_t index = cls.id();
157+
const classid_t cid = cls.id();
158+
ASSERT(!IsTopLevelCid(cid));
153159

154160
// During the transition period we would like [SharedClassTable] to operate in
155161
// parallel to [ClassTable].
@@ -158,12 +164,12 @@ void ClassTable::Register(const Class& cls) {
158164
cls.is_abstract() ? 0 : Class::host_instance_size(cls.raw());
159165

160166
const intptr_t expected_cid =
161-
shared_class_table_->Register(index, instance_size);
167+
shared_class_table_->Register(cid, instance_size);
162168

163-
if (index != kIllegalCid) {
164-
ASSERT(index > 0 && index < kNumPredefinedCids && index < top_);
165-
ASSERT(table_.load()[index] == nullptr);
166-
table_.load()[index] = cls.raw();
169+
if (cid != kIllegalCid) {
170+
ASSERT(cid > 0 && cid < kNumPredefinedCids && cid < top_);
171+
ASSERT(table_.load()[cid] == nullptr);
172+
table_.load()[cid] = cls.raw();
167173
} else {
168174
if (top_ == capacity_) {
169175
const intptr_t new_capacity = capacity_ + kCapacityIncrement;
@@ -177,6 +183,28 @@ void ClassTable::Register(const Class& cls) {
177183
ASSERT(expected_cid == cls.id());
178184
}
179185

186+
void ClassTable::RegisterTopLevel(const Class& cls) {
187+
if (top_ >= std::numeric_limits<classid_t>::max()) {
188+
FATAL1("Fatal error in ClassTable::RegisterTopLevel: invalid index %" Pd
189+
"\n",
190+
top_);
191+
}
192+
193+
ASSERT(Thread::Current()->IsMutatorThread());
194+
195+
const intptr_t index = cls.id();
196+
ASSERT(index == kIllegalCid);
197+
198+
if (tlc_top_ == tlc_capacity_) {
199+
const intptr_t new_capacity = tlc_capacity_ + kCapacityIncrement;
200+
GrowTopLevel(new_capacity);
201+
}
202+
ASSERT(tlc_top_ < tlc_capacity_);
203+
cls.set_id(ClassTable::CidFromTopLevelIndex(tlc_top_));
204+
tlc_table_.load()[tlc_top_] = cls.raw();
205+
tlc_top_++; // Increment next index.
206+
}
207+
180208
intptr_t SharedClassTable::Register(intptr_t index, intptr_t size) {
181209
if (!Class::is_valid_id(top_)) {
182210
FATAL1("Fatal error in SharedClassTable::Register: invalid index %" Pd "\n",
@@ -202,6 +230,11 @@ intptr_t SharedClassTable::Register(intptr_t index, intptr_t size) {
202230
}
203231

204232
void ClassTable::AllocateIndex(intptr_t index) {
233+
if (IsTopLevelCid(index)) {
234+
AllocateTopLevelIndex(index);
235+
return;
236+
}
237+
205238
// This is called by a snapshot reader.
206239
shared_class_table_->AllocateIndex(index);
207240
ASSERT(Class::is_valid_id(index));
@@ -220,6 +253,21 @@ void ClassTable::AllocateIndex(intptr_t index) {
220253
ASSERT(capacity_ == shared_class_table_->capacity_);
221254
}
222255

256+
void ClassTable::AllocateTopLevelIndex(intptr_t cid) {
257+
ASSERT(IsTopLevelCid(cid));
258+
const intptr_t tlc_index = IndexFromTopLevelCid(cid);
259+
260+
if (tlc_index >= tlc_capacity_) {
261+
const intptr_t new_capacity = tlc_index + kCapacityIncrement;
262+
GrowTopLevel(new_capacity);
263+
}
264+
265+
ASSERT(tlc_table_.load()[tlc_index] == nullptr);
266+
if (tlc_index >= tlc_top_) {
267+
tlc_top_ = tlc_index + 1;
268+
}
269+
}
270+
223271
void ClassTable::Grow(intptr_t new_capacity) {
224272
ASSERT(new_capacity > capacity_);
225273

@@ -243,6 +291,29 @@ void ClassTable::Grow(intptr_t new_capacity) {
243291
capacity_ = new_capacity;
244292
}
245293

294+
void ClassTable::GrowTopLevel(intptr_t new_capacity) {
295+
ASSERT(new_capacity > tlc_capacity_);
296+
297+
auto old_table = tlc_table_.load();
298+
auto new_table = static_cast<ClassPtr*>(
299+
malloc(new_capacity * sizeof(ClassPtr))); // NOLINT
300+
intptr_t i;
301+
for (i = 0; i < tlc_capacity_; i++) {
302+
// Don't use memmove, which changes this from a relaxed atomic operation
303+
// to a non-atomic operation.
304+
new_table[i] = old_table[i];
305+
}
306+
for (; i < new_capacity; i++) {
307+
// Don't use memset, which changes this from a relaxed atomic operation
308+
// to a non-atomic operation.
309+
new_table[i] = 0;
310+
}
311+
old_class_tables_->Add(old_table);
312+
313+
tlc_table_.store(new_table);
314+
tlc_capacity_ = new_capacity;
315+
}
316+
246317
void SharedClassTable::AllocateIndex(intptr_t index) {
247318
// This is called by a snapshot reader.
248319
ASSERT(Class::is_valid_id(index));
@@ -320,9 +391,16 @@ void SharedClassTable::Grow(intptr_t new_capacity) {
320391
capacity_ = new_capacity;
321392
}
322393

323-
void ClassTable::Unregister(intptr_t index) {
324-
shared_class_table_->Unregister(index);
325-
table_.load()[index] = nullptr;
394+
void ClassTable::Unregister(intptr_t cid) {
395+
ASSERT(!IsTopLevelCid(cid));
396+
shared_class_table_->Unregister(cid);
397+
table_.load()[cid] = nullptr;
398+
}
399+
400+
void ClassTable::UnregisterTopLevel(intptr_t cid) {
401+
ASSERT(IsTopLevelCid(cid));
402+
const intptr_t tlc_index = IndexFromTopLevelCid(cid);
403+
tlc_table_.load()[tlc_index] = nullptr;
326404
}
327405

328406
void SharedClassTable::Unregister(intptr_t index) {
@@ -376,6 +454,12 @@ void ClassTable::VisitObjectPointers(ObjectPointerVisitor* visitor) {
376454
ObjectPtr* to = reinterpret_cast<ObjectPtr*>(&table[top_ - 1]);
377455
visitor->VisitPointers(from, to);
378456
}
457+
if (tlc_top_ != 0) {
458+
auto* tlc_table = tlc_table_.load();
459+
ObjectPtr* from = reinterpret_cast<ObjectPtr*>(&tlc_table[0]);
460+
ObjectPtr* to = reinterpret_cast<ObjectPtr*>(&tlc_table[tlc_top_ - 1]);
461+
visitor->VisitPointers(from, to);
462+
}
379463
visitor->clear_gc_root_type();
380464
}
381465

@@ -420,13 +504,18 @@ void ClassTable::Print() {
420504
}
421505
}
422506

423-
void ClassTable::SetAt(intptr_t index, ClassPtr raw_cls) {
507+
void ClassTable::SetAt(intptr_t cid, ClassPtr raw_cls) {
508+
if (IsTopLevelCid(cid)) {
509+
tlc_table_.load()[IndexFromTopLevelCid(cid)] = raw_cls;
510+
return;
511+
}
512+
424513
// This is called by snapshot reader and class finalizer.
425-
ASSERT(index < capacity_);
514+
ASSERT(cid < capacity_);
426515
const intptr_t size =
427516
raw_cls == nullptr ? 0 : Class::host_instance_size(raw_cls);
428-
shared_class_table_->SetSizeAt(index, size);
429-
table_.load()[index] = raw_cls;
517+
shared_class_table_->SetSizeAt(cid, size);
518+
table_.load()[cid] = raw_cls;
430519
}
431520

432521
#ifndef PRODUCT

0 commit comments

Comments
 (0)