Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 2c52480

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Introduction of type testing stubs - Part 4
In order to avoid generating type testing stubs for too many types in the system - and thereby potentially cause an increase in code size - this change introduces a smarter way to decide for which types we should generate optimized type testing stubs. The precompiler creates a [TypeUsageInfo] which we use to collect information. More specifically: a) We collect the destination types for all type checks we emit (we do this inside AssertAssignableInstr::EmitNativeCode). -> These are types we might want to generate optimized type testing stubs for. b) We collect type argument vectors used in instance creations (we do this inside AllocateObjectInstr::EmitNativeCode) and keep a set of of used type argument vectors for each class. After the precompiler has finished compiling normal code we scan the set of destination types collected in a) for uninstantiated types (or more specifically, type parameter types). We then propagate the type argument vectors used on object allocation sites, which were collected in b), in order to find out what kind of types are flowing into those type parameters. This allows us to extend the set of types which we test against, by adding the types that flow into type parameters. We use this final augmented set of destination types as a "filter" when making the decision whether to generate an optimized type testing stub for a given type. Issue dart-lang/sdk#32603 Measured impact on flutter HEAD-HEAD-HEAD with TTS Part 1 - 4 applied (2018-04-03): * stock build benchmark: around 4% improvement * gallery app.so size: -2.68% (13987348 -> 13612928) * gallery memory: no sigificant changes: - SubtypeTestCache: - 10kb - ObjectPool: + 6 kb - Type: no change (probably due to wasted alignment slot before) - TypeParameter: + 4 kb (can get rid of the field here later) * gallery AOT compile-time: measured +1.3%, inside flakiness range Change-Id: I12a398d18f970ba2db741913bb47b0f36ae38d58 Reviewed-on: https://dart-review.googlesource.com/48640 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Régis Crelier <[email protected]>
1 parent 25f98bc commit 2c52480

File tree

14 files changed

+842
-29
lines changed

14 files changed

+842
-29
lines changed

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,10 @@ void Precompiler::DoCompileAll(
272272

273273
ClassFinalizer::SortClasses();
274274

275+
// Collects type usage information which allows us to decide when/how to
276+
// optimize runtime type tests.
277+
TypeUsageInfo type_usage_info(T);
278+
275279
// The cid-ranges of subclasses of a class are e.g. used for is/as checks
276280
// as well as other type checks.
277281
HierarchyInfo hierarchy_info(T);
@@ -1897,6 +1901,12 @@ void Precompiler::AttachOptimizedTypeTestingStub() {
18971901
// "dynamic") we need to mark it as writable.
18981902
Dart::vm_isolate()->heap()->WriteProtect(false);
18991903

1904+
TypeUsageInfo* type_usage_info = Thread::Current()->type_usage_info();
1905+
1906+
// At this point we're not generating any new code, so we build a picture of
1907+
// which types we might type-test against.
1908+
type_usage_info->BuildTypeUsageInformation();
1909+
19001910
TypeTestingStubGenerator type_testing_stubs;
19011911
Instructions& instr = Instructions::Handle();
19021912
for (intptr_t i = 0; i < types.length(); i++) {
@@ -1906,8 +1916,14 @@ void Precompiler::AttachOptimizedTypeTestingStub() {
19061916
// corresponding to the class id, in Dart source code.
19071917
if (type.IsResolved() && !type.IsMalformedOrMalbounded() &&
19081918
(!type.IsType() || type.type_class_id() != kVectorCid)) {
1909-
instr = type_testing_stubs.OptimizedCodeForType(type);
1910-
type.SetTypeTestingStub(instr);
1919+
if (type_usage_info->IsUsedInTypeTest(type) || type.IsDynamicType() ||
1920+
type.IsObjectType()) {
1921+
instr = type_testing_stubs.OptimizedCodeForType(type);
1922+
type.SetTypeTestingStub(instr);
1923+
1924+
// Ensure we retain the type.
1925+
AddType(type);
1926+
}
19111927
}
19121928
}
19131929
Dart::vm_isolate()->heap()->WriteProtect(true);

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "vm/stub_code.h"
3232
#include "vm/symbols.h"
3333
#include "vm/timeline.h"
34+
#include "vm/type_testing_stubs.h"
3435

3536
namespace dart {
3637

@@ -1917,11 +1918,15 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
19171918
const Register dst_type_reg,
19181919
const Register scratch_reg,
19191920
Label* done) {
1921+
TypeUsageInfo* type_usage_info = thread()->type_usage_info();
1922+
19201923
// If the int type is assignable to [dst_type] we special case it on the
19211924
// caller side!
19221925
const Type& int_type = Type::Handle(zone(), Type::IntType());
1926+
bool is_non_smi = false;
19231927
if (int_type.IsSubtypeOf(dst_type, NULL, NULL, Heap::kOld)) {
19241928
__ BranchIfSmi(instance_reg, done);
1929+
is_non_smi = true;
19251930
}
19261931

19271932
// We can handle certain types very efficiently on the call site (with a
@@ -1938,20 +1943,29 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
19381943
__ LoadField(dst_type_reg,
19391944
FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset(
19401945
type_param.index())));
1946+
if (type_usage_info != NULL) {
1947+
type_usage_info->UseTypeInAssertAssignable(dst_type);
1948+
}
19411949
} else {
19421950
HierarchyInfo* hi = Thread::Current()->hierarchy_info();
19431951
if (hi != NULL) {
19441952
const Class& type_class = Class::Handle(zone(), dst_type.type_class());
19451953

1954+
bool check_handled_at_callsite = false;
19461955
bool used_cid_range_check = false;
19471956
const bool can_use_simple_cid_range_test =
19481957
hi->CanUseSubtypeRangeCheckFor(dst_type);
19491958
if (can_use_simple_cid_range_test) {
19501959
const CidRangeVector& ranges = hi->SubtypeRangesForClass(type_class);
19511960
if (ranges.length() <= kMaxNumberOfCidRangesToTest) {
1952-
__ LoadClassIdMayBeSmi(scratch_reg, instance_reg);
1953-
GenerateSubtypeRangeCheck(scratch_reg, type_class, done);
1961+
if (is_non_smi) {
1962+
__ LoadClassId(scratch_reg, instance_reg);
1963+
} else {
1964+
__ LoadClassIdMayBeSmi(scratch_reg, instance_reg);
1965+
}
1966+
GenerateCidRangesCheck(assembler(), scratch_reg, ranges, done);
19541967
used_cid_range_check = true;
1968+
check_handled_at_callsite = true;
19551969
}
19561970
}
19571971

@@ -1961,6 +1975,14 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
19611975
GenerateListTypeCheck(scratch_reg, done);
19621976
used_cid_range_check = true;
19631977
}
1978+
1979+
// If we haven't handled the positive case of the type check on the
1980+
// call-site, we want an optimized type testing stub and therefore record
1981+
// it in the [TypeUsageInfo].
1982+
if (!check_handled_at_callsite) {
1983+
ASSERT(type_usage_info != NULL);
1984+
type_usage_info->UseTypeInAssertAssignable(dst_type);
1985+
}
19641986
}
19651987
__ LoadObject(dst_type_reg, dst_type);
19661988
}

runtime/vm/compiler/backend/flow_graph_compiler.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ class FlowGraphCompiler : public ValueObject {
287287
// Accessors.
288288
Assembler* assembler() const { return assembler_; }
289289
const ParsedFunction& parsed_function() const { return parsed_function_; }
290+
const Function& function() const { return parsed_function_.function(); }
290291
const GrowableArray<BlockEntryInstr*>& block_order() const {
291292
return block_order_;
292293
}

runtime/vm/compiler/backend/il.cc

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "vm/scopes.h"
2727
#include "vm/stub_code.h"
2828
#include "vm/symbols.h"
29+
#include "vm/type_testing_stubs.h"
2930

3031
#include "vm/compiler/backend/il_printer.h"
3132

@@ -45,23 +46,28 @@ DEFINE_FLAG(bool,
4546
"Support unboxed double and float32x4 fields.");
4647
DECLARE_FLAG(bool, eliminate_type_checks);
4748

48-
const CidRangeVector& HierarchyInfo::SubtypeRangesForClass(const Class& klass) {
49-
ClassTable* table = thread_->isolate()->class_table();
49+
const CidRangeVector& HierarchyInfo::SubtypeRangesForClass(
50+
const Class& klass,
51+
bool include_abstract) {
52+
ClassTable* table = thread()->isolate()->class_table();
5053
const intptr_t cid_count = table->NumCids();
51-
if (cid_subtype_ranges_ == NULL) {
52-
cid_subtype_ranges_ = new CidRangeVector[cid_count];
54+
CidRangeVector** cid_ranges =
55+
include_abstract ? &cid_subtype_ranges_abstract_ : &cid_subtype_ranges_;
56+
if (*cid_ranges == NULL) {
57+
*cid_ranges = new CidRangeVector[cid_count];
5358
}
5459

55-
CidRangeVector& ranges = cid_subtype_ranges_[klass.id()];
60+
CidRangeVector& ranges = (*cid_ranges)[klass.id()];
5661
if (ranges.length() == 0) {
57-
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/true);
62+
BuildRangesFor(table, &ranges, klass, /*use_subtype_test=*/true,
63+
include_abstract);
5864
}
5965
return ranges;
6066
}
6167

6268
const CidRangeVector& HierarchyInfo::SubclassRangesForClass(
6369
const Class& klass) {
64-
ClassTable* table = thread_->isolate()->class_table();
70+
ClassTable* table = thread()->isolate()->class_table();
6571
const intptr_t cid_count = table->NumCids();
6672
if (cid_subclass_ranges_ == NULL) {
6773
cid_subclass_ranges_ = new CidRangeVector[cid_count];
@@ -77,8 +83,9 @@ const CidRangeVector& HierarchyInfo::SubclassRangesForClass(
7783
void HierarchyInfo::BuildRangesFor(ClassTable* table,
7884
CidRangeVector* ranges,
7985
const Class& klass,
80-
bool use_subtype_test) {
81-
Zone* zone = thread_->zone();
86+
bool use_subtype_test,
87+
bool include_abstract) {
88+
Zone* zone = thread()->zone();
8289
Class& cls = Class::Handle(zone);
8390

8491
// Only really used if `use_subtype_test == true`.
@@ -91,16 +98,16 @@ void HierarchyInfo::BuildRangesFor(ClassTable* table,
9198
for (intptr_t cid = kInstanceCid; cid < cid_count; ++cid) {
9299
// Create local zone because deep hierarchies may allocate lots of handles
93100
// within one iteration of this loop.
94-
StackZone stack_zone(thread_);
95-
HANDLESCOPE(thread_);
101+
StackZone stack_zone(thread());
102+
HANDLESCOPE(thread());
96103

97104
if (!table->HasValidClassAt(cid)) continue;
98105
if (cid == kTypeArgumentsCid) continue;
99106
if (cid == kVoidCid) continue;
100107
if (cid == kDynamicCid) continue;
101108
if (cid == kNullCid) continue;
102109
cls = table->At(cid);
103-
if (cls.is_abstract()) continue;
110+
if (!include_abstract && cls.is_abstract()) continue;
104111
if (cls.is_patch()) continue;
105112
if (cls.IsTopLevel()) continue;
106113

@@ -148,7 +155,7 @@ bool HierarchyInfo::CanUseSubtypeRangeCheckFor(const AbstractType& type) {
148155
return false;
149156
}
150157

151-
Zone* zone = thread_->zone();
158+
Zone* zone = thread()->zone();
152159
const Class& type_class = Class::Handle(zone, type.type_class());
153160

154161
// The FutureOr<T> type cannot be handled by checking whether the instance is
@@ -194,7 +201,7 @@ bool HierarchyInfo::CanUseGenericSubtypeRangeCheckFor(
194201
// [CanUseSubtypeRangeCheckFor], since we handle type parameters in the type
195202
// expression in some cases (see below).
196203

197-
Zone* zone = thread_->zone();
204+
Zone* zone = thread()->zone();
198205
const Class& type_class = Class::Handle(zone, type.type_class());
199206
const intptr_t num_type_parameters = type_class.NumTypeParameters();
200207
const intptr_t num_type_arguments = type_class.NumTypeArguments();
@@ -236,9 +243,7 @@ bool HierarchyInfo::CanUseGenericSubtypeRangeCheckFor(
236243
AbstractType& type_arg = AbstractType::Handle(zone);
237244
for (intptr_t i = 0; i < num_type_parameters; ++i) {
238245
type_arg = ta.TypeAt(num_type_arguments - num_type_parameters + i);
239-
// NOTE: We can handle type parameters but it increases size by a
240-
// significant amount.
241-
if (!CanUseSubtypeRangeCheckFor(type_arg)) {
246+
if (!CanUseSubtypeRangeCheckFor(type_arg) && !type_arg.IsTypeParameter()) {
242247
return false;
243248
}
244249
}
@@ -250,7 +255,8 @@ bool HierarchyInfo::InstanceOfHasClassRange(const AbstractType& type,
250255
intptr_t* lower_limit,
251256
intptr_t* upper_limit) {
252257
if (CanUseSubtypeRangeCheckFor(type)) {
253-
const Class& type_class = Class::Handle(thread_->zone(), type.type_class());
258+
const Class& type_class =
259+
Class::Handle(thread()->zone(), type.type_class());
254260
const CidRangeVector& ranges = SubtypeRangesForClass(type_class);
255261
if (ranges.length() == 1) {
256262
const CidRange& range = ranges[0];
@@ -3791,6 +3797,14 @@ void StaticCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
37913797
ArgumentsInfo args_info(type_args_len(), ArgumentCount(), argument_names());
37923798
compiler->GenerateStaticCall(deopt_id(), token_pos(), function(), args_info,
37933799
locs(), *call_ic_data, rebind_rule_);
3800+
if (function().IsFactory()) {
3801+
TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
3802+
if (type_usage_info != nullptr) {
3803+
const Class& klass = Class::Handle(function().Owner());
3804+
RegisterTypeArgumentsUse(compiler->function(), type_usage_info, klass,
3805+
ArgumentAt(0));
3806+
}
3807+
}
37943808
#else
37953809
const Array& arguments_descriptor = Array::Handle(
37963810
zone, (ic_data() == NULL) ? GetArgumentsDescriptor()

runtime/vm/compiler/backend/il.h

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Range;
3737
class RangeAnalysis;
3838
class RangeBoundary;
3939
class UnboxIntegerInstr;
40+
class TypeUsageInfo;
4041

4142
// CompileType describes type of the value produced by the definition.
4243
//
@@ -351,23 +352,27 @@ class HierarchyInfo : public StackResource {
351352
public:
352353
explicit HierarchyInfo(Thread* thread)
353354
: StackResource(thread),
354-
thread_(thread),
355355
cid_subtype_ranges_(NULL),
356+
cid_subtype_ranges_abstract_(NULL),
356357
cid_subclass_ranges_(NULL) {
357358
thread->set_hierarchy_info(this);
358359
}
359360

360361
~HierarchyInfo() {
361-
thread_->set_hierarchy_info(NULL);
362+
thread()->set_hierarchy_info(NULL);
362363

363364
delete[] cid_subtype_ranges_;
364365
cid_subtype_ranges_ = NULL;
365366

367+
delete[] cid_subtype_ranges_abstract_;
368+
cid_subtype_ranges_abstract_ = NULL;
369+
366370
delete[] cid_subclass_ranges_;
367371
cid_subclass_ranges_ = NULL;
368372
}
369373

370-
const CidRangeVector& SubtypeRangesForClass(const Class& klass);
374+
const CidRangeVector& SubtypeRangesForClass(const Class& klass,
375+
bool include_abstract = false);
371376
const CidRangeVector& SubclassRangesForClass(const Class& klass);
372377

373378
bool InstanceOfHasClassRange(const AbstractType& type,
@@ -396,10 +401,11 @@ class HierarchyInfo : public StackResource {
396401
void BuildRangesFor(ClassTable* table,
397402
CidRangeVector* ranges,
398403
const Class& klass,
399-
bool use_subtype_test);
404+
bool use_subtype_test,
405+
bool include_abstract = false);
400406

401-
Thread* thread_;
402407
CidRangeVector* cid_subtype_ranges_;
408+
CidRangeVector* cid_subtype_ranges_abstract_;
403409
CidRangeVector* cid_subclass_ranges_;
404410
};
405411

runtime/vm/compiler/backend/il_arm.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "vm/stack_frame.h"
2323
#include "vm/stub_code.h"
2424
#include "vm/symbols.h"
25+
#include "vm/type_testing_stubs.h"
2526

2627
#define __ compiler->assembler()->
2728
#define Z (compiler->zone())
@@ -2433,6 +2434,14 @@ static void InlineArrayAllocation(FlowGraphCompiler* compiler,
24332434
}
24342435

24352436
void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2437+
TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
2438+
if (type_usage_info != nullptr) {
2439+
const Class& list_class = Class::Handle(
2440+
compiler->thread()->isolate()->class_table()->At(kArrayCid));
2441+
RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
2442+
element_type()->definition());
2443+
}
2444+
24362445
const Register kLengthReg = R2;
24372446
const Register kElemTypeReg = R1;
24382447
const Register kResultReg = R0;
@@ -6389,6 +6398,13 @@ LocationSummary* AllocateObjectInstr::MakeLocationSummary(Zone* zone,
63896398
}
63906399

63916400
void AllocateObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
6401+
if (ArgumentCount() == 1) {
6402+
TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
6403+
if (type_usage_info != nullptr) {
6404+
RegisterTypeArgumentsUse(compiler->function(), type_usage_info, cls_,
6405+
ArgumentAt(0));
6406+
}
6407+
}
63926408
const Code& stub = Code::ZoneHandle(
63936409
compiler->zone(), StubCode::GetAllocationStubForClass(cls()));
63946410
const StubEntry stub_entry(stub);

runtime/vm/compiler/backend/il_arm64.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "vm/stack_frame.h"
2222
#include "vm/stub_code.h"
2323
#include "vm/symbols.h"
24+
#include "vm/type_testing_stubs.h"
2425

2526
#define __ compiler->assembler()->
2627
#define Z (compiler->zone())
@@ -2190,6 +2191,14 @@ static void InlineArrayAllocation(FlowGraphCompiler* compiler,
21902191
}
21912192

21922193
void CreateArrayInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
2194+
TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
2195+
if (type_usage_info != nullptr) {
2196+
const Class& list_class = Class::Handle(
2197+
compiler->thread()->isolate()->class_table()->At(kArrayCid));
2198+
RegisterTypeArgumentsUse(compiler->function(), type_usage_info, list_class,
2199+
element_type()->definition());
2200+
}
2201+
21932202
const Register kLengthReg = R2;
21942203
const Register kElemTypeReg = R1;
21952204
const Register kResultReg = R0;
@@ -5597,6 +5606,13 @@ LocationSummary* AllocateObjectInstr::MakeLocationSummary(Zone* zone,
55975606
}
55985607

55995608
void AllocateObjectInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
5609+
if (ArgumentCount() == 1) {
5610+
TypeUsageInfo* type_usage_info = compiler->thread()->type_usage_info();
5611+
if (type_usage_info != nullptr) {
5612+
RegisterTypeArgumentsUse(compiler->function(), type_usage_info, cls_,
5613+
ArgumentAt(0));
5614+
}
5615+
}
56005616
const Code& stub = Code::ZoneHandle(
56015617
compiler->zone(), StubCode::GetAllocationStubForClass(cls()));
56025618
const StubEntry stub_entry(stub);

0 commit comments

Comments
 (0)