Skip to content

Commit f226c22

Browse files
mkustermanncommit-bot@chromium.org
authored andcommitted
[VM] Introduction of type testing stubs - Part 2
This CL starts building type testing stubs specialzed for [Type] objects we test against. More specifically, it adds support for: * Handling obvious fast cases on the call sites (while still having a call to stub for negative case) * Handling type tests against type parameters, by loading the value of the type parameter on the call sites and invoking it's type testing stub. * Specialzed type testing stubs for instantiated types where we can do [CidRange]-based subtype-checks. ==> e.g. String/List<dynamic> * Specialzed type testing stubs for instantiated types where we can do [CidRange]-based subclass-checks for the class and [CidRange]-based subtype-checks for the type arguments. ==> e.g. Widget<State>, where we know [Widget] is only extended and not implemented. * Specialzed type testing stubs for certain non-instantiated types where we can do [CidRange]-based subclass-checks for the class and [CidRange]-based subtype-checks for the instantiated type arguments and cid based comparisons for type parameters. (Note that this fast-case migth result in some false-negatives!) ==> e.g. _HashMapEntry<K, V>, where we know [_HashMapEntry] is only extended and not implemented. This optimizes cases where the caller uses `new HashMap<A, B>()` and only uses `A` and `B` as key/values (and not subclasses of it). The false-negative can occur when subtypes of A or B are used. In such cases we fall back to the [SubtypeTestCache]-based imlementation. Issue #31798 Change-Id: Ic1853977bf55d815755b0d652ec8e20e51efb4cf Reviewed-on: https://dart-review.googlesource.com/44788 Reviewed-by: Vyacheslav Egorov <[email protected]> Reviewed-by: Régis Crelier <[email protected]>
1 parent 165c583 commit f226c22

13 files changed

+831
-27
lines changed

runtime/vm/compiler/aot/precompiler.cc

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include "vm/timeline.h"
4747
#include "vm/timer.h"
4848
#include "vm/type_table.h"
49+
#include "vm/type_testing_stubs.h"
4950
#include "vm/unicode.h"
5051
#include "vm/version.h"
5152

@@ -310,6 +311,10 @@ void Precompiler::DoCompileAll(
310311
// Compile newly found targets and add their callees until we reach a
311312
// fixed point.
312313
Iterate();
314+
315+
// Replace the default type testing stubs installed on [Type]s with new
316+
// [Type]-specialized stubs.
317+
AttachOptimizedTypeTestingStub();
313318
}
314319

315320
I->set_compilation_allowed(false);
@@ -1856,6 +1861,61 @@ void Precompiler::DropFields() {
18561861
}
18571862
}
18581863

1864+
void Precompiler::AttachOptimizedTypeTestingStub() {
1865+
Isolate::Current()->heap()->CollectAllGarbage();
1866+
GrowableHandlePtrArray<const AbstractType> types(Z, 200);
1867+
{
1868+
class TypesCollector : public ObjectVisitor {
1869+
public:
1870+
explicit TypesCollector(Zone* zone,
1871+
GrowableHandlePtrArray<const AbstractType>* types)
1872+
: type_(AbstractType::Handle(zone)), types_(types) {}
1873+
1874+
void VisitObject(RawObject* obj) {
1875+
if (obj->GetClassId() == kTypeCid || obj->GetClassId() == kTypeRefCid) {
1876+
type_ ^= obj;
1877+
types_->Add(type_);
1878+
}
1879+
}
1880+
1881+
private:
1882+
AbstractType& type_;
1883+
GrowableHandlePtrArray<const AbstractType>* types_;
1884+
};
1885+
1886+
HeapIterationScope his(T);
1887+
TypesCollector visitor(Z, &types);
1888+
1889+
// Find all type objects in this isolate.
1890+
I->heap()->VisitObjects(&visitor);
1891+
1892+
// Find all type objects in the vm-isolate.
1893+
Dart::vm_isolate()->heap()->VisitObjects(&visitor);
1894+
}
1895+
1896+
// Since we are potentially changing type objects in the vm-isolate (e.g.
1897+
// "dynamic") we need to mark it as writable.
1898+
Dart::vm_isolate()->heap()->WriteProtect(false);
1899+
1900+
TypeTestingStubGenerator type_testing_stubs;
1901+
Instructions& instr = Instructions::Handle();
1902+
for (intptr_t i = 0; i < types.length(); i++) {
1903+
const AbstractType& type = types.At(i);
1904+
1905+
// [kVectorCid] is excluded because it doesn't have a real class,
1906+
// corresponding to the class id, in Dart source code.
1907+
if (type.IsResolved() && !type.IsMalformedOrMalbounded() &&
1908+
(!type.IsType() || type.type_class_id() != kVectorCid)) {
1909+
instr = type_testing_stubs.OptimizedCodeForType(type);
1910+
type.SetTypeTestingStub(instr);
1911+
}
1912+
}
1913+
Dart::vm_isolate()->heap()->WriteProtect(true);
1914+
1915+
RELEASE_ASSERT(Object::dynamic_type().type_test_stub_entry_point() !=
1916+
StubCode::DefaultTypeTest_entry()->EntryPoint());
1917+
}
1918+
18591919
void Precompiler::DropTypes() {
18601920
ObjectStore* object_store = I->object_store();
18611921
GrowableObjectArray& retained_types =

runtime/vm/compiler/aot/precompiler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,8 @@ class Precompiler : public ValueObject {
373373
void TraceConstFunctions();
374374
void CollectCallbackFields();
375375

376+
void AttachOptimizedTypeTestingStub();
377+
376378
void TraceForRetainedFunctions();
377379
void DropFunctions();
378380
void DropFields();

runtime/vm/compiler/assembler/assembler_arm64.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ class Assembler : public ValueObject {
440440

441441
void LoadField(Register dst, FieldAddress address) { ldr(dst, address); }
442442

443+
void CompareWithFieldValue(Register value, FieldAddress address) {
444+
ldr(TMP, address);
445+
cmp(value, Operand(TMP));
446+
}
447+
443448
// Misc. functionality
444449
intptr_t CodeSize() const { return buffer_.Size(); }
445450
intptr_t prologue_offset() const { return prologue_offset_; }

runtime/vm/compiler/assembler/assembler_ia32.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ class Assembler : public ValueObject {
566566

567567
void CompareRegisters(Register a, Register b);
568568
void BranchIf(Condition condition, Label* label) { j(condition, label); }
569+
void LoadField(Register dst, FieldAddress address) { movw(dst, address); }
569570

570571
// Issues a move instruction if 'to' is not the same as 'from'.
571572
void MoveRegister(Register to, Register from);

runtime/vm/compiler/backend/flow_graph_compiler.cc

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1910,7 +1910,50 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
19101910
__ BranchIfSmi(instance_reg, done);
19111911
}
19121912

1913-
__ LoadObject(dst_type_reg, dst_type);
1913+
// We can handle certain types very efficiently on the call site (with a
1914+
// bailout to the normal stub, which will do a runtime call).
1915+
if (dst_type.IsTypeParameter()) {
1916+
const TypeParameter& type_param = TypeParameter::Cast(dst_type);
1917+
const Register kTypeArgumentsReg = type_param.IsClassTypeParameter()
1918+
? instantiator_type_args_reg
1919+
: function_type_args_reg;
1920+
1921+
// Check if type arguments are null, i.e. equivalent to vector of dynamic.
1922+
__ CompareObject(kTypeArgumentsReg, Object::null_object());
1923+
__ BranchIf(EQUAL, done);
1924+
__ LoadField(dst_type_reg,
1925+
FieldAddress(kTypeArgumentsReg, TypeArguments::type_at_offset(
1926+
type_param.index())));
1927+
} else {
1928+
HierarchyInfo* hi = Thread::Current()->hierarchy_info();
1929+
if (hi != NULL) {
1930+
const Class& type_class = Class::Handle(zone(), dst_type.type_class());
1931+
1932+
// We can use [dst_name_reg], there is no overlap of use.
1933+
const Register scratch_reg = dst_name_reg;
1934+
1935+
bool used_cid_range_check = false;
1936+
const bool can_use_simple_cid_range_test =
1937+
hi->CanUseSubtypeRangeCheckFor(dst_type);
1938+
if (can_use_simple_cid_range_test) {
1939+
const CidRangeVector& ranges = hi->SubtypeRangesForClass(type_class);
1940+
if (ranges.length() <= kMaxNumberOfCidRangesToTest) {
1941+
__ LoadClassIdMayBeSmi(scratch_reg, instance_reg);
1942+
GenerateSubtypeRangeCheck(scratch_reg, type_class, done);
1943+
used_cid_range_check = true;
1944+
}
1945+
}
1946+
1947+
if (!used_cid_range_check && can_use_simple_cid_range_test &&
1948+
IsListClass(type_class)) {
1949+
__ LoadClassIdMayBeSmi(scratch_reg, instance_reg);
1950+
GenerateListTypeCheck(scratch_reg, done);
1951+
used_cid_range_check = true;
1952+
}
1953+
}
1954+
__ LoadObject(dst_type_reg, dst_type);
1955+
}
1956+
19141957
__ LoadObject(dst_name_reg, dst_name);
19151958
__ LoadObject(subtype_cache_reg,
19161959
SubtypeTestCache::ZoneHandle(zone(), SubtypeTestCache::New()));

runtime/vm/compiler/backend/flow_graph_compiler_arm.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -725,7 +725,6 @@ void FlowGraphCompiler::GenerateAssertAssignableAOT(
725725
kInstantiatorTypeArgumentsReg,
726726
kFunctionTypeArgumentsReg, kSubtypeTestCacheReg,
727727
kDstTypeReg, kDstNameReg, &done);
728-
729728
__ LoadField(R9,
730729
FieldAddress(kDstTypeReg,
731730
AbstractType::type_test_stub_entry_point_offset()));

runtime/vm/compiler/backend/il.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ void HierarchyInfo::BuildRangesFor(ClassTable* table,
143143
bool HierarchyInfo::CanUseSubtypeRangeCheckFor(const AbstractType& type) {
144144
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
145145

146-
if (!type.IsInstantiated() || type.IsFunctionType() ||
146+
if (!type.IsInstantiated() || !type.IsType() || type.IsFunctionType() ||
147147
type.IsDartFunctionType()) {
148148
return false;
149149
}
@@ -174,7 +174,7 @@ bool HierarchyInfo::CanUseGenericSubtypeRangeCheckFor(
174174
const AbstractType& type) {
175175
ASSERT(type.IsFinalized() && !type.IsMalformedOrMalbounded());
176176

177-
if (type.IsFunctionType() || type.IsDartFunctionType()) {
177+
if (!type.IsType() || type.IsFunctionType() || type.IsDartFunctionType()) {
178178
return false;
179179
}
180180

runtime/vm/object_store.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ class ObjectPointerVisitor;
125125
R_(Code, megamorphic_miss_code) \
126126
R_(Function, megamorphic_miss_function) \
127127
RW(Array, obfuscation_map) \
128+
RW(GrowableObjectArray, type_testing_stubs) \
128129
RW(GrowableObjectArray, changed_in_last_reload) \
129130
// Please remember the last entry must be referred in the 'to' function below.
130131

runtime/vm/stub_code_arm.cc

Lines changed: 50 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
#include "vm/object_store.h"
1818
#include "vm/runtime_entry.h"
1919
#include "vm/stack_frame.h"
20-
#include "vm/stub_code.h"
2120
#include "vm/tags.h"
2221
#include "vm/type_testing_stubs.h"
2322

@@ -1865,20 +1864,10 @@ void StubCode::GenerateDefaultTypeTestStub(Assembler* assembler) {
18651864
Label done;
18661865

18671866
const Register kInstanceReg = R0;
1868-
const Register kDstTypeReg = R8;
1869-
18701867
// Fast case for 'null'.
18711868
__ CompareObject(kInstanceReg, Object::null_object());
18721869
__ BranchIf(EQUAL, &done);
18731870

1874-
// Fast case for 'int'.
1875-
Label not_smi;
1876-
__ BranchIfNotSmi(kInstanceReg, &not_smi);
1877-
__ CompareObject(kDstTypeReg, Object::ZoneHandle(Type::IntType()));
1878-
__ BranchIf(EQUAL, &done);
1879-
__ Bind(&not_smi);
1880-
1881-
// Tail call the [SubtypeTestCache]-based implementation.
18821871
__ ldr(CODE_REG, Address(THR, Thread::slow_type_test_stub_offset()));
18831872
__ ldr(R9, FieldAddress(CODE_REG, Code::entry_point_offset()));
18841873
__ bx(R9);
@@ -1887,6 +1876,56 @@ void StubCode::GenerateDefaultTypeTestStub(Assembler* assembler) {
18871876
__ Ret();
18881877
}
18891878

1879+
void TypeTestingStubGenerator::BuildOptimizedTypeTestStub(
1880+
Assembler* assembler,
1881+
HierarchyInfo* hi,
1882+
const Type& type,
1883+
const Class& type_class) {
1884+
const Register kInstanceReg = R0;
1885+
const Register kClassIdReg = R9;
1886+
1887+
BuildOptimizedTypeTestStubFastCases(assembler, hi, type, type_class,
1888+
kInstanceReg, kClassIdReg);
1889+
1890+
__ ldr(CODE_REG, Address(THR, Thread::slow_type_test_stub_offset()));
1891+
__ ldr(TMP, FieldAddress(CODE_REG, Code::entry_point_offset()));
1892+
__ bx(TMP);
1893+
}
1894+
1895+
void TypeTestingStubGenerator::
1896+
BuildOptimizedSubclassRangeCheckWithTypeArguments(Assembler* assembler,
1897+
HierarchyInfo* hi,
1898+
const Class& type_class,
1899+
const TypeArguments& tp,
1900+
const TypeArguments& ta) {
1901+
const Register kInstanceReg = R0;
1902+
const Register kInstanceTypeArguments = NOTFP;
1903+
const Register kClassIdReg = R9;
1904+
1905+
BuildOptimizedSubclassRangeCheckWithTypeArguments(
1906+
assembler, hi, type_class, tp, ta, kClassIdReg, kInstanceReg,
1907+
kInstanceTypeArguments);
1908+
}
1909+
1910+
void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
1911+
Assembler* assembler,
1912+
HierarchyInfo* hi,
1913+
const AbstractType& type_arg,
1914+
intptr_t type_param_value_offset_i,
1915+
Label* check_failed) {
1916+
const Register kInstantiatorTypeArgumentsReg = R2;
1917+
const Register kFunctionTypeArgumentsReg = R1;
1918+
const Register kInstanceTypeArguments = NOTFP;
1919+
1920+
const Register kClassIdReg = R9;
1921+
const Register kOwnTypeArgumentValue = TMP;
1922+
1923+
BuildOptimizedTypeArgumentValueCheck(
1924+
assembler, hi, type_arg, type_param_value_offset_i, kClassIdReg,
1925+
kInstanceTypeArguments, kInstantiatorTypeArgumentsReg,
1926+
kFunctionTypeArgumentsReg, kOwnTypeArgumentValue, check_failed);
1927+
}
1928+
18901929
void StubCode::GenerateUnreachableTypeTestStub(Assembler* assembler) {
18911930
__ Breakpoint();
18921931
}

runtime/vm/stub_code_arm64.cc

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include "vm/stack_frame.h"
1717
#include "vm/stub_code.h"
1818
#include "vm/tags.h"
19+
#include "vm/type_testing_stubs.h"
1920

2021
#define __ assembler->
2122

@@ -1934,6 +1935,56 @@ void StubCode::GenerateUnreachableTypeTestStub(Assembler* assembler) {
19341935
__ Breakpoint();
19351936
}
19361937

1938+
void TypeTestingStubGenerator::BuildOptimizedTypeTestStub(
1939+
Assembler* assembler,
1940+
HierarchyInfo* hi,
1941+
const Type& type,
1942+
const Class& type_class) {
1943+
const Register kInstanceReg = R0;
1944+
const Register kClassIdReg = R9;
1945+
1946+
BuildOptimizedTypeTestStubFastCases(assembler, hi, type, type_class,
1947+
kInstanceReg, kClassIdReg);
1948+
1949+
__ ldr(CODE_REG, Address(THR, Thread::slow_type_test_stub_offset()));
1950+
__ ldr(R9, FieldAddress(CODE_REG, Code::entry_point_offset()));
1951+
__ br(R9);
1952+
}
1953+
1954+
void TypeTestingStubGenerator::
1955+
BuildOptimizedSubclassRangeCheckWithTypeArguments(Assembler* assembler,
1956+
HierarchyInfo* hi,
1957+
const Class& type_class,
1958+
const TypeArguments& tp,
1959+
const TypeArguments& ta) {
1960+
const Register kInstanceReg = R0;
1961+
const Register kInstanceTypeArguments = R7;
1962+
const Register kClassIdReg = R9;
1963+
1964+
BuildOptimizedSubclassRangeCheckWithTypeArguments(
1965+
assembler, hi, type_class, tp, ta, kClassIdReg, kInstanceReg,
1966+
kInstanceTypeArguments);
1967+
}
1968+
1969+
void TypeTestingStubGenerator::BuildOptimizedTypeArgumentValueCheck(
1970+
Assembler* assembler,
1971+
HierarchyInfo* hi,
1972+
const AbstractType& type_arg,
1973+
intptr_t type_param_value_offset_i,
1974+
Label* check_failed) {
1975+
const Register kInstantiatorTypeArgumentsReg = R1;
1976+
const Register kFunctionTypeArgumentsReg = R2;
1977+
const Register kInstanceTypeArguments = R7;
1978+
1979+
const Register kClassIdReg = R9;
1980+
const Register kOwnTypeArgumentValue = TMP;
1981+
1982+
BuildOptimizedTypeArgumentValueCheck(
1983+
assembler, hi, type_arg, type_param_value_offset_i, kClassIdReg,
1984+
kInstanceTypeArguments, kInstantiatorTypeArgumentsReg,
1985+
kFunctionTypeArgumentsReg, kOwnTypeArgumentValue, check_failed);
1986+
}
1987+
19371988
void StubCode::GenerateSlowTypeTestStub(Assembler* assembler) {
19381989
Label done, call_runtime;
19391990

0 commit comments

Comments
 (0)