Skip to content

Commit cf8a606

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Improve AssertAssignable/InstanceOf by having a fast case for non-generic, instantiated types
When the type to test against is instantiated and has no type arguments there is a high probability that we receive instances of that class or subclasses at runtime. This CL therefore extends the fast-path of AssertAssignable/InstanceOf by checking whether the instance class id is within the cid ranges that directly/indirectly implement/extend the type to test against. Currently we have an almost depth-first preorder numbering of class ids in AOT, but there are exceptions. So each class can have a number of cid-ranges as subclasses / classes which implement it's interface. This seems to improve performance of dart-aot-v2 * flutter stock build by 15+% * DeltaBlueClosures by 10+% and reduces code size on * flutter gallery by -3% Issue #31798 Change-Id: I07dd91589cc3fcd8c5952bdba339e2e2a459e08e Reviewed-on: https://dart-review.googlesource.com/35620 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Régis Crelier <[email protected]> Reviewed-by: Vyacheslav Egorov <[email protected]>
1 parent 2be7b95 commit cf8a606

19 files changed

+329
-211
lines changed

runtime/vm/class_table.cc

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -190,15 +190,6 @@ void ClassTable::AllocateIndex(intptr_t index) {
190190
}
191191
}
192192

193-
void ClassTable::RegisterAt(intptr_t index, const Class& cls) {
194-
ASSERT(Thread::Current()->IsMutatorThread());
195-
ASSERT(index != kIllegalCid);
196-
ASSERT(index >= kNumPredefinedCids);
197-
AllocateIndex(index);
198-
cls.set_id(index);
199-
table_[index] = cls.raw();
200-
}
201-
202193
#if defined(DEBUG)
203194
void ClassTable::Unregister(intptr_t index) {
204195
table_[index] = 0;

runtime/vm/class_table.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,6 @@ class ClassTable {
191191

192192
void AllocateIndex(intptr_t index);
193193

194-
void RegisterAt(intptr_t index, const Class& cls);
195-
196194
#if defined(DEBUG)
197195
void Unregister(intptr_t index);
198196
#endif

runtime/vm/compiler/aot/aot_call_specializer.cc

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -994,13 +994,13 @@ bool AotCallSpecializer::TryReplaceInstanceOfWithRangeCheck(
994994
return false;
995995
}
996996

997-
TypeRangeCache* cache = precompiler_->type_range_cache();
998-
if (cache == NULL) {
997+
HierarchyInfo* hi = thread()->hierarchy_info();
998+
if (hi == NULL) {
999999
return false;
10001000
}
10011001

10021002
intptr_t lower_limit, upper_limit;
1003-
if (!cache->InstanceOfHasClassRange(type, &lower_limit, &upper_limit)) {
1003+
if (!hi->InstanceOfHasClassRange(type, &lower_limit, &upper_limit)) {
10041004
return false;
10051005
}
10061006

@@ -1068,13 +1068,13 @@ bool AotCallSpecializer::TryReplaceTypeCastWithRangeCheck(
10681068
return false;
10691069
}
10701070

1071-
TypeRangeCache* cache = precompiler_->type_range_cache();
1072-
if (cache == NULL) {
1071+
HierarchyInfo* hi = thread()->hierarchy_info();
1072+
if (hi == NULL) {
10731073
return false;
10741074
}
10751075

10761076
intptr_t lower_limit, upper_limit;
1077-
if (!cache->InstanceOfHasClassRange(type, &lower_limit, &upper_limit)) {
1077+
if (!hi->InstanceOfHasClassRange(type, &lower_limit, &upper_limit)) {
10781078
return false;
10791079
}
10801080

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 4 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -211,13 +211,6 @@ TypeRangeCache::TypeRangeCache(Precompiler* precompiler,
211211
lower_limits_[i] = kNotComputed;
212212
upper_limits_[i] = kNotComputed;
213213
}
214-
ASSERT(precompiler->type_range_cache() == NULL);
215-
precompiler->set_type_range_cache(this);
216-
}
217-
218-
TypeRangeCache::~TypeRangeCache() {
219-
ASSERT(precompiler_->type_range_cache() == this);
220-
precompiler_->set_type_range_cache(NULL);
221214
}
222215

223216
RawError* Precompiler::CompileAll(
@@ -235,98 +228,6 @@ RawError* Precompiler::CompileAll(
235228
}
236229
}
237230

238-
bool TypeRangeCache::InstanceOfHasClassRange(const AbstractType& type,
239-
intptr_t* lower_limit,
240-
intptr_t* upper_limit) {
241-
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
242-
243-
if (!type.IsInstantiated()) return false;
244-
if (type.IsFunctionType()) return false;
245-
if (type.IsDartFunctionType()) return false;
246-
247-
Zone* zone = thread_->zone();
248-
const TypeArguments& type_arguments =
249-
TypeArguments::Handle(zone, type.arguments());
250-
if (!type_arguments.IsNull() &&
251-
!type_arguments.IsRaw(0, type_arguments.Length()))
252-
return false;
253-
254-
intptr_t type_cid = type.type_class_id();
255-
if (lower_limits_[type_cid] == kNotContiguous) return false;
256-
if (lower_limits_[type_cid] != kNotComputed) {
257-
*lower_limit = lower_limits_[type_cid];
258-
*upper_limit = upper_limits_[type_cid];
259-
return true;
260-
}
261-
262-
*lower_limit = -1;
263-
*upper_limit = -1;
264-
intptr_t last_matching_cid = -1;
265-
266-
ClassTable* table = thread_->isolate()->class_table();
267-
Class& cls = Class::Handle(zone);
268-
AbstractType& cls_type = AbstractType::Handle(zone);
269-
for (intptr_t cid = kInstanceCid; cid < table->NumCids(); cid++) {
270-
// Create local zone because deep hierarchies may allocate lots of handles
271-
// within one iteration of this loop.
272-
StackZone stack_zone(thread_);
273-
HANDLESCOPE(thread_);
274-
275-
if (!table->HasValidClassAt(cid)) continue;
276-
if (cid == kTypeArgumentsCid) continue;
277-
if (cid == kVoidCid) continue;
278-
if (cid == kDynamicCid) continue;
279-
if (cid == kNullCid) continue; // Instance is not at Bottom like Null type.
280-
cls = table->At(cid);
281-
if (cls.is_abstract()) continue;
282-
if (cls.is_patch()) continue;
283-
if (cls.IsTopLevel()) continue;
284-
285-
cls_type = cls.RareType();
286-
if (cls_type.IsSubtypeOf(type, NULL, NULL, Heap::kNew)) {
287-
last_matching_cid = cid;
288-
if (*lower_limit == -1) {
289-
// Found beginning of range.
290-
*lower_limit = cid;
291-
} else if (*upper_limit == -1) {
292-
// Expanding range.
293-
} else {
294-
// Found a second range.
295-
lower_limits_[type_cid] = kNotContiguous;
296-
return false;
297-
}
298-
} else {
299-
if (*lower_limit == -1) {
300-
// Still before range.
301-
} else if (*upper_limit == -1) {
302-
// Found end of range.
303-
*upper_limit = last_matching_cid;
304-
} else {
305-
// After range.
306-
}
307-
}
308-
}
309-
if (*lower_limit == -1) {
310-
// Not implemented by any concrete class.
311-
*lower_limit = kIllegalCid;
312-
*upper_limit = kIllegalCid;
313-
}
314-
315-
if (*upper_limit == -1) {
316-
ASSERT(last_matching_cid != -1);
317-
*upper_limit = last_matching_cid;
318-
}
319-
320-
if (FLAG_trace_precompiler) {
321-
THR_Print("Type check for %s is cid range [%" Pd ", %" Pd "]\n",
322-
type.ToCString(), *lower_limit, *upper_limit);
323-
}
324-
325-
lower_limits_[type_cid] = *lower_limit;
326-
upper_limits_[type_cid] = *upper_limit;
327-
return true;
328-
}
329-
330231
Precompiler::Precompiler(Thread* thread)
331232
: thread_(thread),
332233
zone_(NULL),
@@ -354,7 +255,6 @@ Precompiler::Precompiler(Thread* thread)
354255
types_to_retain_(),
355256
consts_to_retain_(),
356257
field_type_map_(),
357-
type_range_cache_(NULL),
358258
error_(Error::Handle()),
359259
get_runtime_type_is_unique_(false) {}
360260

@@ -375,7 +275,10 @@ void Precompiler::DoCompileAll(
375275
ASSERT(Error::Handle(Z, T->sticky_error()).IsNull());
376276

377277
ClassFinalizer::SortClasses();
378-
TypeRangeCache trc(this, T, I->class_table()->NumCids());
278+
279+
// The cid-ranges of subclasses of a class are e.g. used for is/as checks
280+
// as well as other type checks.
281+
HierarchyInfo hierarchy_info(T);
379282

380283
// Precompile static initializers to compute result type information.
381284
PrecompileStaticInitializers();

runtime/vm/compiler/aot/precompiler.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,10 +343,6 @@ class Precompiler : public ValueObject {
343343
}
344344

345345
FieldTypeMap* field_type_map() { return &field_type_map_; }
346-
TypeRangeCache* type_range_cache() { return type_range_cache_; }
347-
void set_type_range_cache(TypeRangeCache* value) {
348-
type_range_cache_ = value;
349-
}
350346

351347
static void PopulateWithICData(const Function& func, FlowGraph* graph);
352348

@@ -433,7 +429,6 @@ class Precompiler : public ValueObject {
433429
AbstractTypeSet types_to_retain_;
434430
InstanceSet consts_to_retain_;
435431
FieldTypeMap field_type_map_;
436-
TypeRangeCache* type_range_cache_;
437432
CidMap feedback_cid_map_;
438433
FunctionFeedbackMap function_feedback_map_;
439434
Error& error_;

runtime/vm/compiler/assembler/assembler_ia32.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,10 @@ class Assembler : public ValueObject {
643643
void AddImmediate(Register reg, const Immediate& imm);
644644
void SubImmediate(Register reg, const Immediate& imm);
645645

646+
void CompareImmediate(Register reg, int32_t immediate) {
647+
cmpl(reg, Immediate(immediate));
648+
}
649+
646650
void Drop(intptr_t stack_elements);
647651

648652
void LoadIsolate(Register dst);

runtime/vm/compiler/assembler/assembler_x64.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,9 @@ class Assembler : public ValueObject {
582582

583583
void CompareImmediate(Register reg, const Immediate& imm);
584584
void CompareImmediate(const Address& address, const Immediate& imm);
585+
void CompareImmediate(Register reg, int32_t immediate) {
586+
return CompareImmediate(reg, Immediate(immediate));
587+
}
585588

586589
void testl(Register reg, const Immediate& imm) { testq(reg, imm); }
587590
void testb(const Address& address, const Immediate& imm);

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ void FlowGraphCompiler::GenerateStaticCall(intptr_t deopt_id,
11481148
}
11491149
}
11501150

1151-
void FlowGraphCompiler::GenerateNumberTypeCheck(Register kClassIdReg,
1151+
void FlowGraphCompiler::GenerateNumberTypeCheck(Register class_id_reg,
11521152
const AbstractType& type,
11531153
Label* is_instance_lbl,
11541154
Label* is_not_instance_lbl) {
@@ -1164,10 +1164,10 @@ void FlowGraphCompiler::GenerateNumberTypeCheck(Register kClassIdReg,
11641164
} else if (type.IsDoubleType()) {
11651165
args.Add(kDoubleCid);
11661166
}
1167-
CheckClassIds(kClassIdReg, args, is_instance_lbl, is_not_instance_lbl);
1167+
CheckClassIds(class_id_reg, args, is_instance_lbl, is_not_instance_lbl);
11681168
}
11691169

1170-
void FlowGraphCompiler::GenerateStringTypeCheck(Register kClassIdReg,
1170+
void FlowGraphCompiler::GenerateStringTypeCheck(Register class_id_reg,
11711171
Label* is_instance_lbl,
11721172
Label* is_not_instance_lbl) {
11731173
assembler()->Comment("StringTypeCheck");
@@ -1176,18 +1176,18 @@ void FlowGraphCompiler::GenerateStringTypeCheck(Register kClassIdReg,
11761176
args.Add(kTwoByteStringCid);
11771177
args.Add(kExternalOneByteStringCid);
11781178
args.Add(kExternalTwoByteStringCid);
1179-
CheckClassIds(kClassIdReg, args, is_instance_lbl, is_not_instance_lbl);
1179+
CheckClassIds(class_id_reg, args, is_instance_lbl, is_not_instance_lbl);
11801180
}
11811181

1182-
void FlowGraphCompiler::GenerateListTypeCheck(Register kClassIdReg,
1182+
void FlowGraphCompiler::GenerateListTypeCheck(Register class_id_reg,
11831183
Label* is_instance_lbl) {
11841184
assembler()->Comment("ListTypeCheck");
11851185
Label unknown;
11861186
GrowableArray<intptr_t> args;
11871187
args.Add(kArrayCid);
11881188
args.Add(kGrowableObjectArrayCid);
11891189
args.Add(kImmutableArrayCid);
1190-
CheckClassIds(kClassIdReg, args, is_instance_lbl, &unknown);
1190+
CheckClassIds(class_id_reg, args, is_instance_lbl, &unknown);
11911191
assembler()->Bind(&unknown);
11921192
}
11931193
#endif // !defined(TARGET_ARCH_DBC)
@@ -1813,7 +1813,7 @@ void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
18131813
int bias = 0;
18141814

18151815
// Value is not Smi.
1816-
EmitTestAndCallLoadCid();
1816+
EmitTestAndCallLoadCid(EmitTestCidRegister());
18171817

18181818
int last_check = which_case_to_skip == length - 1 ? length - 2 : length - 1;
18191819

@@ -1832,7 +1832,8 @@ void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
18321832
Label next_test;
18331833
if (!complete || !is_last_check) {
18341834
bias = EmitTestAndCallCheckCid(is_last_check ? failed : &next_test,
1835-
targets[i], bias);
1835+
EmitTestCidRegister(), targets[i], bias,
1836+
/*jump_on_miss =*/true);
18361837
}
18371838
// Do not use the code from the function, but let the code be patched so
18381839
// that we can record the outgoing edges to other code.
@@ -1852,6 +1853,42 @@ void FlowGraphCompiler::EmitTestAndCall(const CallTargets& targets,
18521853
token_index, locs, try_index);
18531854
}
18541855
}
1856+
1857+
bool FlowGraphCompiler::GenerateSubclassTypeCheck(Register class_id_reg,
1858+
const Class& type_class,
1859+
Label* is_subtype) {
1860+
HierarchyInfo* hi = Thread::Current()->hierarchy_info();
1861+
if (hi != NULL) {
1862+
// We test up to 4 different cid ranges, if we would need to test more in
1863+
// order to get a definite answer we fall back to the old mechanism (namely
1864+
// of going into the subtyping cache)
1865+
static const intptr_t kMaxNumberOfCidRangesToTest = 4;
1866+
1867+
const CidRangeVector& ranges = hi->SubtypeRangesForClass(type_class);
1868+
if (ranges.length() <= kMaxNumberOfCidRangesToTest) {
1869+
Label fail;
1870+
int bias = 0;
1871+
for (intptr_t i = 0; i < ranges.length(); ++i) {
1872+
const CidRange& range = ranges[i];
1873+
if (!range.IsIllegalRange()) {
1874+
bias = EmitTestAndCallCheckCid(is_subtype, class_id_reg, range, bias,
1875+
/*jump_on_miss=*/false);
1876+
}
1877+
}
1878+
__ Bind(&fail);
1879+
return true;
1880+
}
1881+
}
1882+
1883+
// We don't have cid-ranges for subclasses, so we'll just test against the
1884+
// class directly if it's non-abstract.
1885+
if (!type_class.is_abstract()) {
1886+
__ CompareImmediate(class_id_reg, type_class.id());
1887+
__ BranchIf(EQUAL, is_subtype);
1888+
}
1889+
return false;
1890+
}
1891+
18551892
#undef __
18561893
#endif
18571894

runtime/vm/compiler/backend/flow_graph_compiler.h

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,13 @@ class FlowGraphCompiler : public ValueObject {
397397
Label* is_not_instance_lbl);
398398
void GenerateListTypeCheck(Register kClassIdReg, Label* is_instance_lbl);
399399

400+
// Returns true if no further checks are necessary but the code coming after
401+
// the emitted code here is still required do a runtime call (for the negative
402+
// case of throwing an exception).
403+
bool GenerateSubclassTypeCheck(Register class_id_reg,
404+
const Class& type_class,
405+
Label* is_subtype_lbl);
406+
400407
void EmitOptimizedInstanceCall(const StubEntry& stub_entry,
401408
const ICData& ic_data,
402409
intptr_t deopt_id,
@@ -651,17 +658,22 @@ class FlowGraphCompiler : public ValueObject {
651658
intptr_t max_immediate);
652659

653660
// More helpers for EmitTestAndCall.
661+
662+
static Register EmitTestCidRegister();
663+
654664
void EmitTestAndCallLoadReceiver(intptr_t count_without_type_args,
655665
const Array& arguments_descriptor);
656666

657667
void EmitTestAndCallSmiBranch(Label* label, bool jump_if_smi);
658668

659-
void EmitTestAndCallLoadCid();
669+
void EmitTestAndCallLoadCid(Register class_id_reg);
660670

661671
// Returns new class-id bias.
662-
int EmitTestAndCallCheckCid(Label* next_label,
672+
int EmitTestAndCallCheckCid(Label* label,
673+
Register class_id_reg,
663674
const CidRange& range,
664-
int bias);
675+
int bias,
676+
bool jump_on_miss = true);
665677

666678
// DBC handles type tests differently from all other architectures due
667679
// to its interpreted nature.

0 commit comments

Comments
 (0)