Skip to content

Commit 6e81d74

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Optimize generation of type testing stubs in JIT mode.
This CL introduces, similar to RawClass::direct_subclasses_, a RawClass::direct_implementors field which is kept up-to-date. The generation of type testing stubs uses this cached reverse-relationship information for faster cid-range calculation (time spent in type testing stub generation is decreased by an order of magnitude). This CL also enable type testing stubs for any instantiated type. This seems to improve * analysis-server-cold-analysis by 6% * analysis-server-warm-analysis by 11% as well as other non-analysis benchmarks in JIT mode. Issue #33257 Change-Id: If01ee06ac08251b2ed27f79d6b82ad7354c8336e Reviewed-on: https://dart-review.googlesource.com/66576 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Alexander Markov <[email protected]> Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent 058510e commit 6e81d74

14 files changed

+394
-27
lines changed

runtime/vm/class_finalizer.cc

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,60 @@ static void CollectFinalizedSuperClasses(
9595
}
9696
}
9797

98+
class InterfaceFinder {
99+
public:
100+
InterfaceFinder(Zone* zone,
101+
ClassTable* class_table,
102+
GrowableArray<intptr_t>* cids)
103+
: class_table_(class_table),
104+
array_handles_(zone),
105+
class_handles_(zone),
106+
type_handles_(zone),
107+
cids_(cids) {}
108+
109+
void FindAllInterfaces(const Class& klass) {
110+
// The class is implementing it's own interface.
111+
cids_->Add(klass.id());
112+
113+
ScopedHandle<Array> array(&array_handles_);
114+
ScopedHandle<Class> interface_class(&class_handles_);
115+
ScopedHandle<Class> current_class(&class_handles_);
116+
ScopedHandle<AbstractType> type(&type_handles_);
117+
118+
*current_class = klass.raw();
119+
while (true) {
120+
// We don't care about top types.
121+
const intptr_t cid = current_class->id();
122+
if (cid == kObjectCid || cid == kDynamicCid || cid == kVoidCid) {
123+
break;
124+
}
125+
126+
// The class is implementing it's directly declared implemented
127+
// interfaces.
128+
*array = klass.interfaces();
129+
if (!array->IsNull()) {
130+
for (intptr_t i = 0; i < array->Length(); ++i) {
131+
*type ^= array->At(i);
132+
*interface_class ^= class_table_->At(type->type_class_id());
133+
FindAllInterfaces(*interface_class);
134+
}
135+
}
136+
137+
// The class is implementing it's super type's interfaces.
138+
*type = current_class->super_type();
139+
if (type->IsNull()) break;
140+
*current_class = class_table_->At(type->type_class_id());
141+
}
142+
}
143+
144+
private:
145+
ClassTable* class_table_;
146+
ReusableHandleStack<Array> array_handles_;
147+
ReusableHandleStack<Class> class_handles_;
148+
ReusableHandleStack<AbstractType> type_handles_;
149+
GrowableArray<intptr_t>* cids_;
150+
};
151+
98152
static void CollectImmediateSuperInterfaces(const Class& cls,
99153
GrowableArray<intptr_t>* cids) {
100154
const Array& interfaces = Array::Handle(cls.interfaces());
@@ -2609,12 +2663,37 @@ void ClassFinalizer::FinalizeTypesInClass(const Class& cls) {
26092663
// Mark as type finalized before resolving type parameter upper bounds
26102664
// in order to break cycles.
26112665
cls.set_is_type_finalized();
2666+
26122667
// Add this class to the direct subclasses of the superclass, unless the
26132668
// superclass is Object.
26142669
if (!super_type.IsNull() && !super_type.IsObjectType()) {
26152670
ASSERT(!super_class.IsNull());
26162671
super_class.AddDirectSubclass(cls);
26172672
}
2673+
2674+
// Add this class as an implementor to the implemented interface's type
2675+
// classes.
2676+
Zone* zone = thread->zone();
2677+
auto& interface_class = Class::Handle(zone);
2678+
for (intptr_t i = 0; i < interface_types.Length(); ++i) {
2679+
interface_type ^= interface_types.At(i);
2680+
interface_class = interface_type.type_class();
2681+
interface_class.AddDirectImplementor(cls);
2682+
}
2683+
2684+
if (FLAG_use_cha_deopt) {
2685+
// Invalidate all CHA code which depends on knowing the implementors of any
2686+
// of the interfaces implemented by this new class.
2687+
ClassTable* class_table = thread->isolate()->class_table();
2688+
GrowableArray<intptr_t> cids;
2689+
InterfaceFinder finder(zone, class_table, &cids);
2690+
finder.FindAllInterfaces(cls);
2691+
for (intptr_t j = 0; j < cids.length(); ++j) {
2692+
interface_class = class_table->At(cids[j]);
2693+
interface_class.DisableCHAImplementorUsers();
2694+
}
2695+
}
2696+
26182697
// A top level class is parsed eagerly so just finalize it.
26192698
if (cls.IsTopLevel()) {
26202699
FinalizeClass(cls);

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2102,8 +2102,9 @@ void Precompiler::TraceTypesFromRetainedClasses() {
21022102
continue; // class 'dynamic' is in the read-only VM isolate.
21032103
}
21042104

2105-
// The subclasses array is only needed for CHA.
2105+
// The subclasses/implementors array is only needed for CHA.
21062106
cls.ClearDirectSubclasses();
2107+
cls.ClearDirectImplementors();
21072108

21082109
bool retain = false;
21092110
members = cls.fields();

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1986,7 +1986,9 @@ void FlowGraphCompiler::GenerateCidRangesCheck(Assembler* assembler,
19861986

19871987
bool FlowGraphCompiler::ShouldUseTypeTestingStubFor(bool optimizing,
19881988
const AbstractType& type) {
1989-
return FLAG_precompiled_mode || (optimizing && type.IsTypeParameter());
1989+
return FLAG_precompiled_mode ||
1990+
(optimizing &&
1991+
(type.IsTypeParameter() || (type.IsType() && type.IsInstantiated())));
19901992
}
19911993

19921994
void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
@@ -2061,8 +2063,11 @@ void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
20612063
// call-site, we want an optimized type testing stub and therefore record
20622064
// it in the [TypeUsageInfo].
20632065
if (!check_handled_at_callsite) {
2064-
ASSERT(type_usage_info != NULL);
2065-
type_usage_info->UseTypeInAssertAssignable(dst_type);
2066+
if (type_usage_info != NULL) {
2067+
type_usage_info->UseTypeInAssertAssignable(dst_type);
2068+
} else {
2069+
ASSERT(!FLAG_precompiled_mode);
2070+
}
20662071
}
20672072
}
20682073
__ LoadObject(dst_type_reg, dst_type);

runtime/vm/compiler/backend/il.cc

Lines changed: 167 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,66 @@ DEFINE_FLAG(bool,
4747
"Support unboxed double and float32x4 fields.");
4848
DECLARE_FLAG(bool, eliminate_type_checks);
4949

50+
class SubclassFinder {
51+
public:
52+
SubclassFinder(Zone* zone,
53+
GrowableArray<intptr_t>* cids,
54+
bool include_abstract)
55+
: array_handles_(zone),
56+
class_handles_(zone),
57+
cids_(cids),
58+
include_abstract_(include_abstract) {}
59+
60+
void ScanSubClasses(const Class& klass) {
61+
if (include_abstract_ || !klass.is_abstract()) {
62+
cids_->Add(klass.id());
63+
}
64+
ScopedHandle<GrowableObjectArray> array(&array_handles_);
65+
ScopedHandle<Class> subclass(&class_handles_);
66+
*array = klass.direct_subclasses();
67+
if (!array->IsNull()) {
68+
for (intptr_t i = 0; i < array->Length(); ++i) {
69+
*subclass ^= array->At(i);
70+
ScanSubClasses(*subclass);
71+
}
72+
}
73+
}
74+
75+
void ScanImplementorClasses(const Class& klass) {
76+
// An implementor of [klass] is
77+
// * the [klass] itself.
78+
// * all implementors of the direct subclasses of [klass].
79+
// * all implementors of the direct implementors of [klass].
80+
if (include_abstract_ || !klass.is_abstract()) {
81+
cids_->Add(klass.id());
82+
}
83+
84+
ScopedHandle<GrowableObjectArray> array(&array_handles_);
85+
ScopedHandle<Class> subclass_or_implementor(&class_handles_);
86+
87+
*array = klass.direct_subclasses();
88+
if (!array->IsNull()) {
89+
for (intptr_t i = 0; i < array->Length(); ++i) {
90+
*subclass_or_implementor ^= (*array).At(i);
91+
ScanImplementorClasses(*subclass_or_implementor);
92+
}
93+
}
94+
*array = klass.direct_implementors();
95+
if (!array->IsNull()) {
96+
for (intptr_t i = 0; i < array->Length(); ++i) {
97+
*subclass_or_implementor ^= (*array).At(i);
98+
ScanImplementorClasses(*subclass_or_implementor);
99+
}
100+
}
101+
}
102+
103+
private:
104+
ReusableHandleStack<GrowableObjectArray> array_handles_;
105+
ReusableHandleStack<Class> class_handles_;
106+
GrowableArray<intptr_t>* cids_;
107+
const bool include_abstract_;
108+
};
109+
50110
const CidRangeVector& HierarchyInfo::SubtypeRangesForClass(
51111
const Class& klass,
52112
bool include_abstract) {
@@ -60,8 +120,13 @@ const CidRangeVector& HierarchyInfo::SubtypeRangesForClass(
60120

61121
CidRangeVector& ranges = (*cid_ranges)[klass.id()];
62122
if (ranges.length() == 0) {
63-
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/true,
64-
include_abstract);
123+
if (!FLAG_precompiled_mode) {
124+
BuildRangesForJIT(table, &ranges, klass, /*use_subtype_test=*/true,
125+
include_abstract);
126+
} else {
127+
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/true,
128+
include_abstract);
129+
}
65130
}
66131
return ranges;
67132
}
@@ -76,7 +141,11 @@ const CidRangeVector& HierarchyInfo::SubclassRangesForClass(
76141

77142
CidRangeVector& ranges = cid_subclass_ranges_[klass.id()];
78143
if (ranges.length() == 0) {
79-
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/false);
144+
if (!FLAG_precompiled_mode) {
145+
BuildRangesForJIT(table, &ranges, klass, /*use_subtype_test=*/true);
146+
} else {
147+
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/false);
148+
}
80149
}
81150
return ranges;
82151
}
@@ -87,12 +156,14 @@ void HierarchyInfo::BuildRangesFor(ClassTable* table,
87156
bool use_subtype_test,
88157
bool include_abstract) {
89158
Zone* zone = thread()->zone();
90-
Class& cls = Class::Handle(zone);
159+
ClassTable* class_table = thread()->isolate()->class_table();
91160

92161
// Only really used if `use_subtype_test == true`.
93162
const Type& dst_type = Type::Handle(zone, Type::RawCast(klass.RareType()));
94163
AbstractType& cls_type = AbstractType::Handle(zone);
95164

165+
Class& cls = Class::Handle(zone);
166+
AbstractType& super_type = AbstractType::Handle(zone);
96167
const intptr_t cid_count = table->NumCids();
97168

98169
intptr_t start = -1;
@@ -123,7 +194,10 @@ void HierarchyInfo::BuildRangesFor(ClassTable* table,
123194
test_succeded = true;
124195
break;
125196
}
126-
cls = cls.SuperClass();
197+
198+
super_type = cls.super_type();
199+
const intptr_t type_class_id = super_type.type_class_id();
200+
cls = class_table->At(type_class_id);
127201
}
128202
}
129203

@@ -148,6 +222,94 @@ void HierarchyInfo::BuildRangesFor(ClassTable* table,
148222
}
149223
}
150224

225+
void HierarchyInfo::BuildRangesForJIT(ClassTable* table,
226+
CidRangeVector* ranges,
227+
const Class& dst_klass,
228+
bool use_subtype_test,
229+
bool include_abstract) {
230+
if (dst_klass.InVMHeap()) {
231+
BuildRangesFor(table, ranges, dst_klass, use_subtype_test,
232+
include_abstract);
233+
return;
234+
}
235+
236+
Zone* zone = thread()->zone();
237+
GrowableArray<intptr_t> cids;
238+
SubclassFinder finder(zone, &cids, include_abstract);
239+
if (use_subtype_test) {
240+
finder.ScanImplementorClasses(dst_klass);
241+
} else {
242+
finder.ScanSubClasses(dst_klass);
243+
}
244+
245+
// Sort all collected cids.
246+
intptr_t* cids_array = cids.data();
247+
248+
qsort(cids_array, cids.length(), sizeof(intptr_t),
249+
[](const void* a, const void* b) {
250+
return static_cast<int>(*static_cast<const intptr_t*>(a) -
251+
*static_cast<const intptr_t*>(b));
252+
});
253+
254+
// Build ranges of all the cids.
255+
Class& klass = Class::Handle();
256+
intptr_t left_cid = -1;
257+
intptr_t last_cid = -1;
258+
for (intptr_t i = 0; i < cids.length(); ++i) {
259+
if (left_cid == -1) {
260+
left_cid = last_cid = cids[i];
261+
} else {
262+
const intptr_t current_cid = cids[i];
263+
264+
// Skip duplicates.
265+
if (current_cid == last_cid) continue;
266+
267+
// Consecutive numbers cids are ok.
268+
if (current_cid == (last_cid + 1)) {
269+
last_cid = current_cid;
270+
} else {
271+
// We sorted, after all!
272+
RELEASE_ASSERT(last_cid < current_cid);
273+
274+
intptr_t j = last_cid + 1;
275+
for (; j < current_cid; ++j) {
276+
if (table->HasValidClassAt(j)) {
277+
klass = table->At(j);
278+
if (!klass.is_patch() && !klass.IsTopLevel()) {
279+
// If we care about abstract classes also, we cannot skip over any
280+
// arbitrary abstract class, only those which are subtypes.
281+
if (include_abstract) {
282+
break;
283+
}
284+
285+
// If the class is concrete we cannot skip over it.
286+
if (!klass.is_abstract()) {
287+
break;
288+
}
289+
}
290+
}
291+
}
292+
293+
if (current_cid == j) {
294+
// If there's only abstract cids between [last_cid] and the
295+
// [current_cid] then we connect them.
296+
last_cid = current_cid;
297+
} else {
298+
// Finish the current open cid range and start a new one.
299+
ranges->Add(CidRange{left_cid, last_cid});
300+
left_cid = last_cid = current_cid;
301+
}
302+
}
303+
}
304+
}
305+
306+
// If there is an open cid-range which we haven't finished yet, we'll
307+
// complete it.
308+
if (left_cid != -1) {
309+
ranges->Add(CidRange{left_cid, last_cid});
310+
}
311+
}
312+
151313
bool HierarchyInfo::CanUseSubtypeRangeCheckFor(const AbstractType& type) {
152314
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
153315

runtime/vm/compiler/backend/il.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,22 @@ class HierarchyInfo : public StackResource {
397397
bool CanUseGenericSubtypeRangeCheckFor(const AbstractType& type);
398398

399399
private:
400+
// Does not use any hierarchy information available in the system but computes
401+
// it via O(n) class table traversal.
400402
void BuildRangesFor(ClassTable* table,
401403
CidRangeVector* ranges,
402404
const Class& klass,
403405
bool use_subtype_test,
404406
bool include_abstract = false);
405407

408+
// In JIT mode we use hierarchy information stored in the [RawClass]s
409+
// direct_subclasses_/direct_implementors_ arrays.
410+
void BuildRangesForJIT(ClassTable* table,
411+
CidRangeVector* ranges,
412+
const Class& klass,
413+
bool use_subtype_test,
414+
bool include_abstract = false);
415+
406416
CidRangeVector* cid_subtype_ranges_;
407417
CidRangeVector* cid_subtype_ranges_abstract_;
408418
CidRangeVector* cid_subclass_ranges_;

0 commit comments

Comments
 (0)