[vm/compiler] Infer type of LoadIndexed from array type

After inlining we may have more accurate information about array being
accessed. This change adds computation of type of LoadIndexed
instruction based on the type of array being accessed.

The computation is able to skip loading of GrowableArray.data.
If array was loaded from a field, it takes static type of a field
into account.

Benchmarks with null safety in JIT mode on x64:
ForInLoop +80-106%
DeltaBlueIterators +15-31%

Change-Id: Ia61aa761f4ccd4692e05fdac043f8a2a46f88f21
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/157823
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Alexander Markov <alexmarkov@google.com>
This commit is contained in:
Alexander Markov 2020-08-10 20:18:53 +00:00 committed by commit-bot@chromium.org
parent 287c796412
commit 501a7ae7b0
5 changed files with 73 additions and 2 deletions

View file

@ -5528,6 +5528,7 @@ class LoadIndexedInstr : public TemplateDefinition<2, NoThrow> {
DECLARE_INSTRUCTION(LoadIndexed)
virtual CompileType ComputeType() const;
virtual bool RecomputeType();
virtual Representation RequiredInputRepresentation(intptr_t idx) const {
ASSERT(idx == 0 || idx == 1);

View file

@ -1701,6 +1701,61 @@ CompileType ExtractNthOutputInstr::ComputeType() const {
return CompileType::FromCid(definition_cid_);
}
static AbstractTypePtr ExtractElementTypeFromArrayType(
const AbstractType& array_type) {
if (array_type.IsTypeParameter()) {
return ExtractElementTypeFromArrayType(
AbstractType::Handle(TypeParameter::Cast(array_type).bound()));
}
if (!array_type.IsType()) {
return Object::dynamic_type().raw();
}
const intptr_t cid = array_type.type_class_id();
if (cid == kGrowableObjectArrayCid || cid == kArrayCid ||
cid == kImmutableArrayCid ||
array_type.type_class() ==
Isolate::Current()->object_store()->list_class()) {
const auto& type_args = TypeArguments::Handle(array_type.arguments());
return type_args.TypeAtNullSafe(Array::kElementTypeTypeArgPos);
}
return Object::dynamic_type().raw();
}
static AbstractTypePtr GetElementTypeFromArray(Value* array) {
return ExtractElementTypeFromArrayType(*(array->Type()->ToAbstractType()));
}
static CompileType ComputeArrayElementType(Value* array) {
// 1. Try to extract element type from array value.
auto& elem_type = AbstractType::Handle(GetElementTypeFromArray(array));
if (!elem_type.IsDynamicType()) {
return CompileType::FromAbstractType(elem_type);
}
// 2. Array value may be loaded from GrowableObjectArray.data.
// Unwrap and try again.
if (auto* load_field = array->definition()->AsLoadField()) {
if (load_field->slot().IsIdentical(Slot::GrowableObjectArray_data())) {
array = load_field->instance();
elem_type = GetElementTypeFromArray(array);
if (!elem_type.IsDynamicType()) {
return CompileType::FromAbstractType(elem_type);
}
}
}
// 3. If array was loaded from a Dart field, use field's static type.
// Unlike propagated type (which could be cid), static type may contain
// type arguments which can be used to figure out element type.
if (auto* load_field = array->definition()->AsLoadField()) {
if (load_field->slot().IsDartField()) {
elem_type =
ExtractElementTypeFromArrayType(load_field->slot().static_type());
}
}
return CompileType::FromAbstractType(elem_type);
}
CompileType LoadIndexedInstr::ComputeType() const {
switch (class_id_) {
case kArrayCid:
@ -1709,7 +1764,7 @@ CompileType LoadIndexedInstr::ComputeType() const {
// The original call knew something.
return *result_type_;
}
return CompileType::Dynamic();
return ComputeArrayElementType(array());
case kTypedDataFloat32ArrayCid:
case kTypedDataFloat64ArrayCid:
@ -1751,4 +1806,13 @@ CompileType LoadIndexedInstr::ComputeType() const {
}
}
bool LoadIndexedInstr::RecomputeType() {
if ((class_id_ == kArrayCid) || (class_id_ == kImmutableArrayCid)) {
// Array element type computation depends on computed
// types of other instructions and may change over time.
return UpdateType(ComputeType());
}
return false;
}
} // namespace dart

View file

@ -9566,6 +9566,9 @@ class Array : public Instance {
bool IsImmutable() const { return raw()->GetClassId() == kImmutableArrayCid; }
// Position of element type in type arguments.
static const intptr_t kElementTypeTypeArgPos = 0;
virtual TypeArgumentsPtr GetTypeArguments() const {
return raw_ptr()->type_arguments_;
}

View file

@ -335,13 +335,15 @@ void ObjectStore::InitKnownObjects() {
}
void ObjectStore::LazyInitCoreTypes() {
if (non_nullable_list_rare_type_ == Type::null()) {
if (list_class_ == Type::null()) {
ASSERT(non_nullable_list_rare_type_ == Type::null());
ASSERT(non_nullable_map_rare_type_ == Type::null());
Thread* thread = Thread::Current();
Zone* zone = thread->zone();
const Library& core_lib = Library::Handle(zone, Library::CoreLibrary());
Class& cls = Class::Handle(zone, core_lib.LookupClass(Symbols::List()));
ASSERT(!cls.IsNull());
set_list_class(cls);
Type& type = Type::Handle(zone);
type ^= cls.RareType();
set_non_nullable_list_rare_type(type);

View file

@ -82,6 +82,7 @@ class ObjectPointerVisitor;
RW(Type, string_type) \
RW(Type, legacy_string_type) \
RW(Type, non_nullable_string_type) \
CW(Class, list_class) /* maybe be null, lazily built */ \
CW(Type, non_nullable_list_rare_type) /* maybe be null, lazily built */ \
CW(Type, non_nullable_map_rare_type) /* maybe be null, lazily built */ \
FW(Type, non_nullable_future_rare_type) /* maybe be null, lazily built */ \