Skip to content

Commit 99a5f14

Browse files
mralephcommit-bot@chromium.org
authored andcommitted
[vm] Enable type stubs based type checks in JIT mode for some types.
Relanding 4be50d6 with fixes to DBC and location summaries: AssertAssignable must save FPU registers. For now we are limiting this to type checks against type parameter types. In Dart 1 mode Dart2JS compiles itself in 28s when running from source and in 23s when running from ideal app-jit snapshot (trained on the same workload). Before this change in Dart 2 mode numbers were 51s and 57s respectively. After this change in Dart 2 mode numbers are 38s and 32s. Meaning that regression is reduced by 50%. Issue #31798 Issue #33257 Change-Id: Ifb55f86453bfdf36a2e03bcd7f3197cfde257103 Reviewed-on: https://dart-review.googlesource.com/57980 Commit-Queue: Vyacheslav Egorov <[email protected]> Reviewed-by: Régis Crelier <[email protected]>
1 parent 90c4215 commit 99a5f14

31 files changed

+501
-232
lines changed

runtime/vm/clustered_snapshot.cc

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ static RawObject* AllocateUninitialized(PageSpace* old_space, intptr_t size) {
3333
return RawObject::FromAddr(address);
3434
}
3535

36+
static bool SnapshotContainsTypeTestingStubs(Snapshot::Kind kind) {
37+
return kind == Snapshot::kFullAOT || kind == Snapshot::kFullJIT;
38+
}
39+
3640
void Deserializer::InitializeHeader(RawObject* raw,
3741
intptr_t class_id,
3842
intptr_t size,
@@ -3058,6 +3062,8 @@ class TypeSerializationCluster : public SerializationCluster {
30583062

30593063
void WriteFill(Serializer* s) {
30603064
const bool is_vm_isolate = s->isolate() == Dart::vm_isolate();
3065+
const bool should_write_type_testing_stub =
3066+
SnapshotContainsTypeTestingStubs(s->kind());
30613067

30623068
intptr_t count = canonical_objects_.length();
30633069
for (intptr_t i = 0; i < count; i++) {
@@ -3069,7 +3075,7 @@ class TypeSerializationCluster : public SerializationCluster {
30693075
}
30703076
s->WriteTokenPosition(type->ptr()->token_pos_);
30713077
s->Write<int8_t>(type->ptr()->type_state_);
3072-
if (s->kind() == Snapshot::kFullAOT) {
3078+
if (should_write_type_testing_stub) {
30733079
RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
30743080
type->ptr()->type_test_stub_entry_point_);
30753081
s->WriteInstructions(instr, Code::null());
@@ -3085,7 +3091,7 @@ class TypeSerializationCluster : public SerializationCluster {
30853091
}
30863092
s->WriteTokenPosition(type->ptr()->token_pos_);
30873093
s->Write<int8_t>(type->ptr()->type_state_);
3088-
if (s->kind() == Snapshot::kFullAOT) {
3094+
if (should_write_type_testing_stub) {
30893095
RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
30903096
type->ptr()->type_test_stub_entry_point_);
30913097
s->WriteInstructions(instr, Code::null());
@@ -3094,7 +3100,7 @@ class TypeSerializationCluster : public SerializationCluster {
30943100

30953101
// The dynamic/void objects are not serialized, so we manually send
30963102
// the type testing stub for it.
3097-
if (s->kind() == Snapshot::kFullAOT && is_vm_isolate) {
3103+
if (should_write_type_testing_stub && is_vm_isolate) {
30983104
RawInstructions* dynamic_instr = type_testing_stubs_.LookupByAddresss(
30993105
Type::dynamic_type().type_test_stub_entry_point());
31003106
s->WriteInstructions(dynamic_instr, Code::null());
@@ -3137,6 +3143,8 @@ class TypeDeserializationCluster : public DeserializationCluster {
31373143

31383144
void ReadFill(Deserializer* d) {
31393145
const bool is_vm_isolate = d->isolate() == Dart::vm_isolate();
3146+
const bool should_read_type_testing_stub =
3147+
SnapshotContainsTypeTestingStubs(d->kind());
31403148

31413149
for (intptr_t id = canonical_start_index_; id < canonical_stop_index_;
31423150
id++) {
@@ -3150,7 +3158,7 @@ class TypeDeserializationCluster : public DeserializationCluster {
31503158
}
31513159
type->ptr()->token_pos_ = d->ReadTokenPosition();
31523160
type->ptr()->type_state_ = d->Read<int8_t>();
3153-
if (d->kind() == Snapshot::kFullAOT) {
3161+
if (should_read_type_testing_stub) {
31543162
instr_ = d->ReadInstructions();
31553163
type_ = type;
31563164
type_.SetTypeTestingStub(instr_);
@@ -3168,7 +3176,7 @@ class TypeDeserializationCluster : public DeserializationCluster {
31683176
}
31693177
type->ptr()->token_pos_ = d->ReadTokenPosition();
31703178
type->ptr()->type_state_ = d->Read<int8_t>();
3171-
if (d->kind() == Snapshot::kFullAOT) {
3179+
if (should_read_type_testing_stub) {
31723180
instr_ = d->ReadInstructions();
31733181
type_ = type;
31743182
type_.SetTypeTestingStub(instr_);
@@ -3177,7 +3185,7 @@ class TypeDeserializationCluster : public DeserializationCluster {
31773185

31783186
// The dynamic/void objects are not serialized, so we manually send
31793187
// the type testing stub for it.
3180-
if (d->kind() == Snapshot::kFullAOT && is_vm_isolate) {
3188+
if (should_read_type_testing_stub && is_vm_isolate) {
31813189
instr_ = d->ReadInstructions();
31823190
Type::dynamic_type().SetTypeTestingStub(instr_);
31833191
instr_ = d->ReadInstructions();
@@ -3186,7 +3194,7 @@ class TypeDeserializationCluster : public DeserializationCluster {
31863194
}
31873195

31883196
void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
3189-
if (kind != Snapshot::kFullAOT) {
3197+
if (!SnapshotContainsTypeTestingStubs(kind)) {
31903198
for (intptr_t id = canonical_start_index_; id < canonical_stop_index_;
31913199
id++) {
31923200
type_ ^= refs.At(id);
@@ -3237,6 +3245,9 @@ class TypeRefSerializationCluster : public SerializationCluster {
32373245
}
32383246

32393247
void WriteFill(Serializer* s) {
3248+
const bool should_write_type_testing_stub =
3249+
SnapshotContainsTypeTestingStubs(s->kind());
3250+
32403251
intptr_t count = objects_.length();
32413252
for (intptr_t i = 0; i < count; i++) {
32423253
RawTypeRef* type = objects_[i];
@@ -3245,7 +3256,7 @@ class TypeRefSerializationCluster : public SerializationCluster {
32453256
for (RawObject** p = from; p <= to; p++) {
32463257
s->WriteRef(*p);
32473258
}
3248-
if (s->kind() == Snapshot::kFullAOT) {
3259+
if (should_write_type_testing_stub) {
32493260
RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
32503261
type->ptr()->type_test_stub_entry_point_);
32513262
s->WriteInstructions(instr, Code::null());
@@ -3276,7 +3287,9 @@ class TypeRefDeserializationCluster : public DeserializationCluster {
32763287
}
32773288

32783289
void ReadFill(Deserializer* d) {
3279-
bool is_vm_object = d->isolate() == Dart::vm_isolate();
3290+
const bool is_vm_object = d->isolate() == Dart::vm_isolate();
3291+
const bool should_read_type_testing_stub =
3292+
SnapshotContainsTypeTestingStubs(d->kind());
32803293

32813294
for (intptr_t id = start_index_; id < stop_index_; id++) {
32823295
RawTypeRef* type = reinterpret_cast<RawTypeRef*>(d->Ref(id));
@@ -3287,14 +3300,24 @@ class TypeRefDeserializationCluster : public DeserializationCluster {
32873300
for (RawObject** p = from; p <= to; p++) {
32883301
*p = d->ReadRef();
32893302
}
3290-
if (d->kind() == Snapshot::kFullAOT) {
3303+
if (should_read_type_testing_stub) {
32913304
instr_ = d->ReadInstructions();
32923305
type_ = type;
32933306
type_.SetTypeTestingStub(instr_);
32943307
}
32953308
}
32963309
}
32973310

3311+
void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
3312+
if (!SnapshotContainsTypeTestingStubs(kind)) {
3313+
for (intptr_t id = start_index_; id < stop_index_; id++) {
3314+
type_ ^= refs.At(id);
3315+
instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);
3316+
type_.SetTypeTestingStub(instr_);
3317+
}
3318+
}
3319+
}
3320+
32983321
private:
32993322
AbstractType& type_;
33003323
Instructions& instr_;
@@ -3331,6 +3354,9 @@ class TypeParameterSerializationCluster : public SerializationCluster {
33313354
}
33323355

33333356
void WriteFill(Serializer* s) {
3357+
const bool should_write_type_testing_stub =
3358+
SnapshotContainsTypeTestingStubs(s->kind());
3359+
33343360
intptr_t count = objects_.length();
33353361
for (intptr_t i = 0; i < count; i++) {
33363362
RawTypeParameter* type = objects_[i];
@@ -3343,7 +3369,7 @@ class TypeParameterSerializationCluster : public SerializationCluster {
33433369
s->WriteTokenPosition(type->ptr()->token_pos_);
33443370
s->Write<int16_t>(type->ptr()->index_);
33453371
s->Write<int8_t>(type->ptr()->type_state_);
3346-
if (s->kind() == Snapshot::kFullAOT) {
3372+
if (should_write_type_testing_stub) {
33473373
RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
33483374
type->ptr()->type_test_stub_entry_point_);
33493375
s->WriteInstructions(instr, Code::null());
@@ -3376,6 +3402,8 @@ class TypeParameterDeserializationCluster : public DeserializationCluster {
33763402

33773403
void ReadFill(Deserializer* d) {
33783404
bool is_vm_object = d->isolate() == Dart::vm_isolate();
3405+
const bool should_read_type_testing_stub =
3406+
SnapshotContainsTypeTestingStubs(d->kind());
33793407

33803408
for (intptr_t id = start_index_; id < stop_index_; id++) {
33813409
RawTypeParameter* type = reinterpret_cast<RawTypeParameter*>(d->Ref(id));
@@ -3390,7 +3418,7 @@ class TypeParameterDeserializationCluster : public DeserializationCluster {
33903418
type->ptr()->token_pos_ = d->ReadTokenPosition();
33913419
type->ptr()->index_ = d->Read<int16_t>();
33923420
type->ptr()->type_state_ = d->Read<int8_t>();
3393-
if (d->kind() == Snapshot::kFullAOT) {
3421+
if (should_read_type_testing_stub) {
33943422
instr_ = d->ReadInstructions();
33953423
type_ = type;
33963424
type_.SetTypeTestingStub(instr_);
@@ -3399,7 +3427,7 @@ class TypeParameterDeserializationCluster : public DeserializationCluster {
33993427
}
34003428

34013429
void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
3402-
if (kind != Snapshot::kFullAOT) {
3430+
if (!SnapshotContainsTypeTestingStubs(kind)) {
34033431
for (intptr_t id = start_index_; id < stop_index_; id++) {
34043432
type_ ^= refs.At(id);
34053433
instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1934,7 +1934,12 @@ void FlowGraphCompiler::GenerateCidRangesCheck(Assembler* assembler,
19341934
}
19351935
}
19361936

1937-
void FlowGraphCompiler::GenerateAssertAssignableAOT(
1937+
bool FlowGraphCompiler::ShouldUseTypeTestingStubFor(bool optimizing,
1938+
const AbstractType& type) {
1939+
return FLAG_precompiled_mode || (optimizing && type.IsTypeParameter());
1940+
}
1941+
1942+
void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
19381943
const AbstractType& dst_type,
19391944
const String& dst_name,
19401945
const Register instance_reg,

runtime/vm/compiler/backend/flow_graph_compiler.h

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -364,21 +364,28 @@ class FlowGraphCompiler : public ValueObject {
364364
const AbstractType& dst_type,
365365
const String& dst_name,
366366
LocationSummary* locs);
367-
void GenerateAssertAssignableAOT(TokenPosition token_pos,
368-
intptr_t deopt_id,
369-
const AbstractType& dst_type,
370-
const String& dst_name,
371-
LocationSummary* locs);
372-
373-
void GenerateAssertAssignableAOT(const AbstractType& dst_type,
374-
const String& dst_name,
375-
const Register instance_reg,
376-
const Register instantiator_type_args_reg,
377-
const Register function_type_args_reg,
378-
const Register subtype_cache_reg,
379-
const Register dst_type_reg,
380-
const Register scratch_reg,
381-
Label* done);
367+
368+
// Returns true if we can use a type testing stub based assert
369+
// assignable code pattern for the given type.
370+
static bool ShouldUseTypeTestingStubFor(bool optimizing,
371+
const AbstractType& type);
372+
373+
void GenerateAssertAssignableViaTypeTestingStub(TokenPosition token_pos,
374+
intptr_t deopt_id,
375+
const AbstractType& dst_type,
376+
const String& dst_name,
377+
LocationSummary* locs);
378+
379+
void GenerateAssertAssignableViaTypeTestingStub(
380+
const AbstractType& dst_type,
381+
const String& dst_name,
382+
const Register instance_reg,
383+
const Register instantiator_type_args_reg,
384+
const Register function_type_args_reg,
385+
const Register subtype_cache_reg,
386+
const Register dst_type_reg,
387+
const Register scratch_reg,
388+
Label* done);
382389

383390
// DBC emits calls very differently from all other architectures due to its
384391
// interpreted nature.

runtime/vm/compiler/backend/flow_graph_compiler_arm.cc

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,8 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
663663
}
664664

665665
if (FLAG_precompiled_mode) {
666-
GenerateAssertAssignableAOT(token_pos, deopt_id, dst_type, dst_name, locs);
666+
GenerateAssertAssignableViaTypeTestingStub(token_pos, deopt_id, dst_type,
667+
dst_name, locs);
667668
} else {
668669
Label is_assignable_fast, is_assignable, runtime_call;
669670

@@ -691,10 +692,11 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
691692
__ PushObject(dst_name); // Push the name of the destination.
692693
__ LoadUniqueObject(R0, test_cache);
693694
__ Push(R0);
694-
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
695+
__ PushObject(Smi::ZoneHandle(zone(), Smi::New(kTypeCheckFromInline)));
696+
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 7, locs);
695697
// Pop the parameters supplied to the runtime entry. The result of the
696698
// type check runtime call is the checked value.
697-
__ Drop(6);
699+
__ Drop(7);
698700
__ Pop(R0);
699701
__ Bind(&is_assignable);
700702
__ PopList((1 << kFunctionTypeArgumentsReg) |
@@ -703,7 +705,7 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
703705
}
704706
}
705707

706-
void FlowGraphCompiler::GenerateAssertAssignableAOT(
708+
void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
707709
TokenPosition token_pos,
708710
intptr_t deopt_id,
709711
const AbstractType& dst_type,
@@ -719,10 +721,10 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
719721

720722
Label done;
721723

722-
GenerateAssertAssignableAOT(dst_type, dst_name, kInstanceReg,
723-
kInstantiatorTypeArgumentsReg,
724-
kFunctionTypeArgumentsReg, kSubtypeTestCacheReg,
725-
kDstTypeReg, kScratchReg, &done);
724+
GenerateAssertAssignableViaTypeTestingStub(
725+
dst_type, dst_name, kInstanceReg, kInstantiatorTypeArgumentsReg,
726+
kFunctionTypeArgumentsReg, kSubtypeTestCacheReg, kDstTypeReg, kScratchReg,
727+
&done);
726728
// We use 2 consecutive entries in the pool for the subtype cache and the
727729
// destination name. The second entry, namely [dst_name] seems to be unused,
728730
// but it will be used by the code throwing a TypeError if the type test fails

runtime/vm/compiler/backend/flow_graph_compiler_arm64.cc

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,9 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
643643
return;
644644
}
645645

646-
if (FLAG_precompiled_mode) {
647-
GenerateAssertAssignableAOT(token_pos, deopt_id, dst_type, dst_name, locs);
646+
if (ShouldUseTypeTestingStubFor(is_optimizing(), dst_type)) {
647+
GenerateAssertAssignableViaTypeTestingStub(token_pos, deopt_id, dst_type,
648+
dst_name, locs);
648649
} else {
649650
Label is_assignable_fast, is_assignable, runtime_call;
650651

@@ -669,18 +670,19 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
669670
__ PushObject(dst_name); // Push the name of the destination.
670671
__ LoadUniqueObject(R0, test_cache);
671672
__ Push(R0);
672-
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
673+
__ PushObject(Smi::ZoneHandle(zone(), Smi::New(kTypeCheckFromInline)));
674+
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 7, locs);
673675
// Pop the parameters supplied to the runtime entry. The result of the
674676
// type check runtime call is the checked value.
675-
__ Drop(6);
677+
__ Drop(7);
676678
__ Pop(R0);
677679
__ Bind(&is_assignable);
678680
__ PopPair(kFunctionTypeArgumentsReg, kInstantiatorTypeArgumentsReg);
679681
__ Bind(&is_assignable_fast);
680682
}
681683
}
682684

683-
void FlowGraphCompiler::GenerateAssertAssignableAOT(
685+
void FlowGraphCompiler::GenerateAssertAssignableViaTypeTestingStub(
684686
TokenPosition token_pos,
685687
intptr_t deopt_id,
686688
const AbstractType& dst_type,
@@ -696,10 +698,10 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
696698

697699
Label done;
698700

699-
GenerateAssertAssignableAOT(dst_type, dst_name, kInstanceReg,
700-
kInstantiatorTypeArgumentsReg,
701-
kFunctionTypeArgumentsReg, kSubtypeTestCacheReg,
702-
kDstTypeReg, kScratchReg, &done);
701+
GenerateAssertAssignableViaTypeTestingStub(
702+
dst_type, dst_name, kInstanceReg, kInstantiatorTypeArgumentsReg,
703+
kFunctionTypeArgumentsReg, kSubtypeTestCacheReg, kDstTypeReg, kScratchReg,
704+
&done);
703705

704706
// We use 2 consecutive entries in the pool for the subtype cache and the
705707
// destination name. The second entry, namely [dst_name] seems to be unused,

runtime/vm/compiler/backend/flow_graph_compiler_ia32.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -686,10 +686,11 @@ void FlowGraphCompiler::GenerateAssertAssignable(TokenPosition token_pos,
686686
__ PushObject(dst_name); // Push the name of the destination.
687687
__ LoadObject(EAX, test_cache);
688688
__ pushl(EAX);
689-
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 6, locs);
689+
__ PushObject(Smi::ZoneHandle(zone(), Smi::New(kTypeCheckFromInline)));
690+
GenerateRuntimeCall(token_pos, deopt_id, kTypeCheckRuntimeEntry, 7, locs);
690691
// Pop the parameters supplied to the runtime entry. The result of the
691692
// type check runtime call is the checked value.
692-
__ Drop(6);
693+
__ Drop(7);
693694
__ popl(EAX);
694695

695696
__ Bind(&is_assignable);

0 commit comments

Comments
 (0)