mirror of
https://github.com/dart-lang/sdk
synced 2024-09-15 22:31:50 +00:00
a70ab07a47
This reverts commit 1016bbadde
.
Reason for revert: Broken benchmarks on golem.
Original change's description:
> [vm/compiler] Add more checks in TTSes for uninstantiated types.
>
> In certain cases, optimized type testing stubs for uninstantiated types
> compare the instantiation of the type arguments for the type against the
> corresponding instance type arguments. Previously, only an identity
> check was generated, so the instantiation had to match the instance type
> argument exactly.
>
> This CL adds checks for the following cases:
> * The instantiated type argument is dynamic, void, or Object.
> * The instance type argument is Null or Never.
>
> When strong null safety is enabled, we also check that the the instance
> type argument is legacy or non-nullable when the instantiated type
> argument is non-nullable Object or that the instantiated type argument
> is nullable or legacy when the instance type argument is Null.
>
> This CL also adds handling for the case where the instantiated or
> instance type argument is not a Type, which is necessary for safely
> retrieving the type class id and nullability, and allocates some
> additional registers in TTSInternalRegs for storing type arguments. On
> ARM7, this requires pushes/pops around type argument checking, because
> we've run out of registers there.
>
> Fixes https://github.com/dart-lang/sdk/issues/40736
>
> TEST=vm/cc/TTS
>
> Bug: https://github.com/dart-lang/sdk/issues/46920
> Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
> Change-Id: Ib8498fd2b9593b4abb92111f062ed2fc95a45c16
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/210681
> Commit-Queue: Tess Strickland <sstrickl@google.com>
> Reviewed-by: Alexander Markov <alexmarkov@google.com>
TBR=kustermann@google.com,alexmarkov@google.com,sstrickl@google.com
Change-Id: I8c2368cbbb3d23aa2d0cb4788737d9b737318bb7
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: https://github.com/dart-lang/sdk/issues/46920
Cq-Include-Trybots: luci.dart.try:vm-kernel-linux-release-x64-try,vm-kernel-precomp-linux-release-x64-try,vm-kernel-precomp-nnbd-linux-release-x64-try,vm-kernel-nnbd-linux-release-x64-try,vm-kernel-linux-product-x64-try,vm-kernel-precomp-linux-product-x64-try,vm-kernel-linux-release-simarm-try,vm-kernel-linux-release-simarm64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/211860
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Tess Strickland <sstrickl@google.com>
390 lines
12 KiB
C++
390 lines
12 KiB
C++
// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#ifndef RUNTIME_VM_TYPE_TESTING_STUBS_H_
|
|
#define RUNTIME_VM_TYPE_TESTING_STUBS_H_
|
|
|
|
#include "vm/object.h"
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
#include "vm/compiler/assembler/assembler.h"
|
|
#include "vm/compiler/backend/il.h"
|
|
#include "vm/compiler/stub_code_compiler.h"
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
namespace dart {
|
|
|
|
class TypeTestingStubNamer {
|
|
public:
|
|
TypeTestingStubNamer();
|
|
|
|
// Simple helper for stringinfying a [type] and prefix it with the type
|
|
// testing
|
|
//
|
|
// (only during dart_boostrap).
|
|
const char* StubNameForType(const AbstractType& type) const;
|
|
|
|
private:
|
|
const char* StringifyType(const AbstractType& type) const;
|
|
static const char* AssemblerSafeName(char* cname);
|
|
|
|
Library& lib_;
|
|
Class& klass_;
|
|
AbstractType& type_;
|
|
String& string_;
|
|
};
|
|
|
|
class TypeTestingStubGenerator {
|
|
public:
|
|
// During bootstrapping it will return `null` for |void| and |dynamic| types,
|
|
// otherwise it will return a default stub which tail-calls
|
|
// subtypingtest/runtime code.
|
|
static CodePtr DefaultCodeForType(const AbstractType& type,
|
|
bool lazy_specialize = true);
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
static CodePtr SpecializeStubFor(Thread* thread, const AbstractType& type);
|
|
#endif
|
|
|
|
TypeTestingStubGenerator();
|
|
|
|
// Creates new stub for [type] (and registers the tuple in object store
|
|
// array) or returns default stub.
|
|
CodePtr OptimizedCodeForType(const AbstractType& type);
|
|
|
|
private:
|
|
#if !defined(TARGET_ARCH_IA32)
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
CodePtr BuildCodeForType(const Type& type);
|
|
static void BuildOptimizedTypeTestStub(
|
|
compiler::Assembler* assembler,
|
|
compiler::UnresolvedPcRelativeCalls* unresolved_calls,
|
|
const Code& slow_type_test_stub,
|
|
HierarchyInfo* hi,
|
|
const Type& type,
|
|
const Class& type_class);
|
|
|
|
static void BuildOptimizedTypeTestStubFastCases(
|
|
compiler::Assembler* assembler,
|
|
HierarchyInfo* hi,
|
|
const Type& type,
|
|
const Class& type_class);
|
|
|
|
static void BuildOptimizedSubtypeRangeCheck(compiler::Assembler* assembler,
|
|
const CidRangeVector& ranges,
|
|
Register class_id_reg,
|
|
compiler::Label* check_succeeded,
|
|
compiler::Label* check_failed);
|
|
|
|
static void BuildOptimizedSubclassRangeCheckWithTypeArguments(
|
|
compiler::Assembler* assembler,
|
|
HierarchyInfo* hi,
|
|
const Type& type,
|
|
const Class& type_class);
|
|
|
|
// Returns whether any cid ranges require type argument checking.
|
|
//
|
|
// If any do, then returns from the stub if any checks that do not need
|
|
// type argument checking succeed, falls through or jumps to load_succeeded if
|
|
// loading the type arguments succeeds, and otherwise jumps to load_failed.
|
|
// That is, code that uses the type arguments should follow immediately.
|
|
//
|
|
// If none do, then falls through or jumps to load_failed if the checks fail,
|
|
// else returns from the stub if the checks are successful. That is, code
|
|
// that handles the failure case (like calling the slow stub) should follow.
|
|
static bool BuildLoadInstanceTypeArguments(
|
|
compiler::Assembler* assembler,
|
|
HierarchyInfo* hi,
|
|
const Type& type,
|
|
const Class& type_class,
|
|
const Register class_id_reg,
|
|
const Register instance_type_args_reg,
|
|
compiler::Label* load_succeeded,
|
|
compiler::Label* load_failed);
|
|
|
|
static void BuildOptimizedTypeArgumentValueCheck(
|
|
compiler::Assembler* assembler,
|
|
HierarchyInfo* hi,
|
|
const AbstractType& type_arg,
|
|
intptr_t type_param_value_offset_i,
|
|
compiler::Label* check_failed);
|
|
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
#endif // !defined(TARGET_ARCH_IA32)
|
|
|
|
TypeTestingStubNamer namer_;
|
|
ObjectStore* object_store_;
|
|
};
|
|
|
|
template <typename T>
|
|
class ReusableHandleStack {
|
|
public:
|
|
explicit ReusableHandleStack(Zone* zone) : zone_(zone), handles_count_(0) {}
|
|
|
|
private:
|
|
T* Obtain() {
|
|
T* handle;
|
|
if (handles_count_ < handles_.length()) {
|
|
handle = handles_[handles_count_];
|
|
} else {
|
|
handle = &T::ZoneHandle(zone_);
|
|
handles_.Add(handle);
|
|
}
|
|
handles_count_++;
|
|
return handle;
|
|
}
|
|
|
|
void Release(T* handle) {
|
|
handles_count_--;
|
|
ASSERT(handles_count_ >= 0);
|
|
ASSERT(handles_[handles_count_] == handle);
|
|
}
|
|
|
|
Zone* zone_;
|
|
|
|
intptr_t handles_count_;
|
|
MallocGrowableArray<T*> handles_;
|
|
|
|
template <typename U>
|
|
friend class ScopedHandle;
|
|
};
|
|
|
|
template <typename T>
|
|
class ScopedHandle {
|
|
public:
|
|
explicit ScopedHandle(ReusableHandleStack<T>* stack)
|
|
: stack_(stack), handle_(stack_->Obtain()) {}
|
|
|
|
~ScopedHandle() { stack_->Release(handle_); }
|
|
|
|
T& operator*() { return *handle_; }
|
|
T* operator->() { return handle_; }
|
|
|
|
private:
|
|
ReusableHandleStack<T>* stack_;
|
|
T* handle_;
|
|
};
|
|
|
|
// Attempts to find a [Class] from un-instantiated [TypeArgument] vector to
|
|
// which it's type parameters are referring to.
|
|
//
|
|
// If the given type argument vector contains references to type parameters,
|
|
// this finder will either return a valid class if all of the type parameters
|
|
// come from the same class and returns `null` otherwise.
|
|
//
|
|
// It is safe to use this class inside loops since the implementation uses a
|
|
// [ReusableHandleStack] (which in pratice will only use a handful of handles).
|
|
class TypeArgumentClassFinder {
|
|
public:
|
|
explicit TypeArgumentClassFinder(Zone* zone)
|
|
: klass_(Class::Handle(zone)),
|
|
type_(AbstractType::Handle(zone)),
|
|
type_arguments_handles_(zone) {}
|
|
|
|
const Class& FindClass(const TypeArguments& ta) {
|
|
klass_ = Class::null();
|
|
|
|
const intptr_t len = ta.Length();
|
|
for (intptr_t i = 0; i < len; ++i) {
|
|
type_ = ta.TypeAt(i);
|
|
if (!FindClassFromType(type_)) {
|
|
klass_ = Class::null();
|
|
break;
|
|
}
|
|
}
|
|
return klass_;
|
|
}
|
|
|
|
private:
|
|
bool FindClassFromType(const AbstractType& type) {
|
|
if (type.IsTypeParameter()) {
|
|
return false;
|
|
} else if (type.IsFunctionType()) {
|
|
// No support for function types yet.
|
|
return false;
|
|
} else if (type.IsTypeRef()) {
|
|
// No support for recursive types.
|
|
return false;
|
|
} else if (type.IsType()) {
|
|
ScopedHandle<TypeArguments> type_arguments(&type_arguments_handles_);
|
|
*type_arguments = Type::Cast(type).arguments();
|
|
const intptr_t len = type_arguments->Length();
|
|
for (intptr_t i = 0; i < len; ++i) {
|
|
type_ = type_arguments->TypeAt(i);
|
|
if (!FindClassFromType(type_)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
UNREACHABLE();
|
|
return false;
|
|
}
|
|
|
|
Class& klass_;
|
|
AbstractType& type_;
|
|
|
|
ReusableHandleStack<TypeArguments> type_arguments_handles_;
|
|
};
|
|
|
|
// Used for instantiating a [TypeArguments] which contains references to type
|
|
// parameters based on an instantiator [TypeArguments] vector.
|
|
//
|
|
// It is safe to use this class inside loops since the implementation uses a
|
|
// [ReusableHandleStack] (which in pratice will only use a handful of handles).
|
|
class TypeArgumentInstantiator {
|
|
public:
|
|
explicit TypeArgumentInstantiator(Zone* zone)
|
|
: klass_(Class::Handle(zone)),
|
|
type_(AbstractType::Handle(zone)),
|
|
instantiator_type_arguments_(TypeArguments::Handle(zone)),
|
|
type_arguments_handles_(zone),
|
|
type_handles_(zone) {}
|
|
|
|
TypeArgumentsPtr Instantiate(
|
|
const Class& klass,
|
|
const TypeArguments& type_arguments,
|
|
const TypeArguments& instantiator_type_arguments) {
|
|
instantiator_type_arguments_ = instantiator_type_arguments.ptr();
|
|
return InstantiateTypeArguments(klass, type_arguments).ptr();
|
|
}
|
|
|
|
private:
|
|
const TypeArguments& InstantiateTypeArguments(
|
|
const Class& klass,
|
|
const TypeArguments& type_arguments);
|
|
|
|
AbstractTypePtr InstantiateType(const AbstractType& type);
|
|
|
|
Class& klass_;
|
|
AbstractType& type_;
|
|
TypeArguments& instantiator_type_arguments_;
|
|
|
|
ReusableHandleStack<TypeArguments> type_arguments_handles_;
|
|
ReusableHandleStack<Type> type_handles_;
|
|
};
|
|
|
|
// Collects data on how [Type] objects are used in generated code.
|
|
class TypeUsageInfo : public ThreadStackResource {
|
|
public:
|
|
explicit TypeUsageInfo(Thread* thread);
|
|
~TypeUsageInfo();
|
|
|
|
void UseTypeInAssertAssignable(const AbstractType& type);
|
|
void UseTypeArgumentsInInstanceCreation(const Class& klass,
|
|
const TypeArguments& ta);
|
|
|
|
// Finalize the collected type usage information.
|
|
void BuildTypeUsageInformation();
|
|
|
|
// Query if [type] is very likely used in a type test (can give
|
|
// false-positives and false-negatives, but tries to make a very good guess)
|
|
bool IsUsedInTypeTest(const AbstractType& type);
|
|
|
|
private:
|
|
template <typename T>
|
|
class ObjectSetTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const T* Key;
|
|
typedef const T* Value;
|
|
typedef const T* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
static inline uword Hash(Key key) { return key->Hash(); }
|
|
};
|
|
|
|
class TypeSetTrait : public ObjectSetTrait<const AbstractType> {
|
|
public:
|
|
static inline bool IsKeyEqual(const AbstractType* pair,
|
|
const AbstractType* key) {
|
|
return pair->Equals(*key);
|
|
}
|
|
};
|
|
|
|
class TypeArgumentsSetTrait : public ObjectSetTrait<const TypeArguments> {
|
|
public:
|
|
static inline bool IsKeyEqual(const TypeArguments* pair,
|
|
const TypeArguments* key) {
|
|
return pair->ptr() == key->ptr();
|
|
}
|
|
};
|
|
|
|
class TypeParameterSetTrait : public ObjectSetTrait<const TypeParameter> {
|
|
public:
|
|
static inline bool IsKeyEqual(const TypeParameter* pair,
|
|
const TypeParameter* key) {
|
|
return pair->ptr() == key->ptr();
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<TypeSetTrait> TypeSet;
|
|
typedef DirectChainedHashMap<TypeArgumentsSetTrait> TypeArgumentsSet;
|
|
typedef DirectChainedHashMap<TypeParameterSetTrait> TypeParameterSet;
|
|
|
|
// Runs an (early terminated) fix-point algorithm which propagates type
|
|
// arguments. For example:
|
|
//
|
|
// class Base<X> {}
|
|
//
|
|
// class Foo<A, B> extends Base<B> {
|
|
// foo() => new Map<List<B>, A>();
|
|
// }
|
|
//
|
|
// main() {
|
|
// new Foo<String, int>();
|
|
// new Map<double, bool>();
|
|
// }
|
|
//
|
|
// will end up adding new type argument vectors to the per-class instantiator
|
|
// type argument vector set:
|
|
//
|
|
// Foo:
|
|
// <int, String, int>
|
|
// Map:
|
|
// <List<int>, String>
|
|
// <double, bool>
|
|
//
|
|
void PropagateTypeArguments(ClassTable* class_table, intptr_t cid_count);
|
|
|
|
// Collects all type parameters we are doing assert assignable checks against.
|
|
void CollectTypeParametersUsedInAssertAssignable(TypeParameterSet* set);
|
|
|
|
// All types which flow into any of the type parameters in [set] will be added
|
|
// to the set of types we test against.
|
|
void UpdateAssertAssignableTypes(ClassTable* class_table,
|
|
intptr_t cid_count,
|
|
TypeParameterSet* set);
|
|
|
|
void AddToSetIfParameter(TypeParameterSet* set,
|
|
const AbstractType* type,
|
|
TypeParameter* param);
|
|
void AddTypeToSet(TypeSet* set, const AbstractType* type);
|
|
|
|
Zone* zone_;
|
|
TypeArgumentClassFinder finder_;
|
|
TypeSet assert_assignable_types_;
|
|
TypeArgumentsSet* instance_creation_arguments_;
|
|
|
|
Class& klass_;
|
|
};
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void RegisterTypeArgumentsUse(const Function& function,
|
|
TypeUsageInfo* type_usage_info,
|
|
const Class& klass,
|
|
Definition* type_arguments);
|
|
#endif
|
|
|
|
#if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
void DeoptimizeTypeTestingStubs();
|
|
|
|
#endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_TYPE_TESTING_STUBS_H_
|