0c9067c626
TEST=runtime/tests/vm/dart/dynamic_module_pragmas_il_test.dart Change-Id: I6efd24f55726db858711d5f77beabf5659e288a4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/371563 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Alexander Markov <alexmarkov@google.com>
279 lines
9.3 KiB
C++
279 lines
9.3 KiB
C++
// Copyright (c) 2012, 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.
|
|
|
|
#include "vm/compiler/cha.h"
|
|
#include "vm/class_table.h"
|
|
#include "vm/compiler/compiler_state.h"
|
|
#include "vm/flags.h"
|
|
#include "vm/log.h"
|
|
#include "vm/object.h"
|
|
#include "vm/raw_object.h"
|
|
#include "vm/visitor.h"
|
|
|
|
namespace dart {
|
|
|
|
void CHA::AddToGuardedClasses(const Class& cls,
|
|
intptr_t subclass_count,
|
|
intptr_t implementor_cid,
|
|
bool track_future) {
|
|
for (intptr_t i = 0; i < guarded_classes_.length(); i++) {
|
|
if (guarded_classes_[i].cls->ptr() == cls.ptr()) {
|
|
if ((subclass_count >= 0) && (guarded_classes_[i].subclass_count == -1)) {
|
|
guarded_classes_[i].subclass_count = subclass_count;
|
|
}
|
|
if ((implementor_cid != kIllegalCid) &&
|
|
(guarded_classes_[i].implementor_cid == kIllegalCid)) {
|
|
guarded_classes_[i].implementor_cid = implementor_cid;
|
|
}
|
|
if (track_future && !guarded_classes_[i].track_future) {
|
|
guarded_classes_[i].track_future = track_future;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
GuardedClassInfo info = {&Class::ZoneHandle(thread_->zone(), cls.ptr()),
|
|
subclass_count, implementor_cid, track_future};
|
|
guarded_classes_.Add(info);
|
|
}
|
|
|
|
void CHA::AddToGuardedClassesForSubclassCount(const Class& cls,
|
|
intptr_t subclass_count) {
|
|
ASSERT(subclass_count >= 0);
|
|
AddToGuardedClasses(cls, subclass_count, kIllegalCid, false);
|
|
}
|
|
|
|
void CHA::AddToGuardedClassesForImplementorCid(const Class& cls,
|
|
intptr_t implementor_cid) {
|
|
ASSERT(implementor_cid != kIllegalCid);
|
|
ASSERT(implementor_cid != kDynamicCid);
|
|
AddToGuardedClasses(cls, -1, implementor_cid, false);
|
|
}
|
|
|
|
void CHA::AddToGuardedClassesToTrackFuture(const Class& cls) {
|
|
AddToGuardedClasses(cls, -1, kIllegalCid, true);
|
|
}
|
|
|
|
bool CHA::IsGuardedClass(intptr_t cid) const {
|
|
for (intptr_t i = 0; i < guarded_classes_.length(); ++i) {
|
|
if (guarded_classes_[i].cls->id() == cid) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CHA::HasSubclasses(const Class& cls) {
|
|
ASSERT(!cls.IsNull());
|
|
ASSERT(!IsInternalOnlyClassId(cls.id()));
|
|
// Can't track dependencies for classes on the VM heap since those are
|
|
// read-only.
|
|
// TODO(fschneider): Enable tracking of CHA dependent code for VM heap
|
|
// classes.
|
|
if (cls.InVMIsolateHeap()) return true;
|
|
|
|
if (cls.IsObjectClass()) {
|
|
// Class Object has subclasses, although we do not keep track of them.
|
|
return true;
|
|
}
|
|
|
|
if (cls.has_dynamically_extendable_subtypes()) return true;
|
|
|
|
Thread* thread = Thread::Current();
|
|
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
|
|
const GrowableObjectArray& direct_subclasses =
|
|
GrowableObjectArray::Handle(cls.direct_subclasses());
|
|
return !direct_subclasses.IsNull() && (direct_subclasses.Length() > 0);
|
|
}
|
|
|
|
bool CHA::HasSubclasses(intptr_t cid) const {
|
|
const ClassTable& class_table = *thread_->isolate_group()->class_table();
|
|
Class& cls = Class::Handle(thread_->zone(), class_table.At(cid));
|
|
return HasSubclasses(cls);
|
|
}
|
|
|
|
bool CHA::ConcreteSubclasses(const Class& cls,
|
|
GrowableArray<intptr_t>* class_ids) {
|
|
if (cls.InVMIsolateHeap()) return false;
|
|
if (cls.IsObjectClass()) return false;
|
|
if (cls.has_dynamically_extendable_subtypes()) return false;
|
|
|
|
if (!cls.is_abstract()) {
|
|
class_ids->Add(cls.id());
|
|
}
|
|
|
|
// This is invoked from precompiler only, we can use unsafe version of
|
|
// Class::direct_subclasses getter.
|
|
ASSERT(FLAG_precompiled_mode);
|
|
const GrowableObjectArray& direct_subclasses =
|
|
GrowableObjectArray::Handle(cls.direct_subclasses_unsafe());
|
|
if (direct_subclasses.IsNull()) {
|
|
return true;
|
|
}
|
|
Class& subclass = Class::Handle();
|
|
for (intptr_t i = 0; i < direct_subclasses.Length(); i++) {
|
|
subclass ^= direct_subclasses.At(i);
|
|
if (!ConcreteSubclasses(subclass, class_ids)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CHA::IsImplemented(const Class& cls) {
|
|
// Can't track dependencies for classes on the VM heap since those are
|
|
// read-only.
|
|
// TODO(fschneider): Enable tracking of CHA dependent code for VM heap
|
|
// classes.
|
|
if (cls.InVMIsolateHeap()) return true;
|
|
if (cls.has_dynamically_extendable_subtypes()) return true;
|
|
|
|
return cls.is_implemented();
|
|
}
|
|
|
|
bool CHA::HasSingleConcreteImplementation(const Class& interface,
|
|
intptr_t* implementation_cid) {
|
|
intptr_t cid = interface.implementor_cid();
|
|
if ((cid == kIllegalCid) || (cid == kDynamicCid) ||
|
|
interface.has_dynamically_extendable_subtypes()) {
|
|
// No implementations / multiple implementations.
|
|
*implementation_cid = kDynamicCid;
|
|
return false;
|
|
}
|
|
|
|
Thread* thread = Thread::Current();
|
|
if (FLAG_use_cha_deopt || thread->isolate_group()->all_classes_finalized()) {
|
|
if (FLAG_trace_cha) {
|
|
THR_Print(" **(CHA) Type has one implementation: %s\n",
|
|
interface.ToCString());
|
|
}
|
|
if (FLAG_use_cha_deopt) {
|
|
CHA& cha = thread->compiler_state().cha();
|
|
cha.AddToGuardedClassesForImplementorCid(interface, cid);
|
|
}
|
|
*implementation_cid = cid;
|
|
return true;
|
|
} else {
|
|
*implementation_cid = kDynamicCid;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool CHA::ClassCanBeFuture(const Class& cls) {
|
|
if (cls.can_be_future()) {
|
|
return true;
|
|
}
|
|
|
|
// Class cannot be Future with the current set of
|
|
// finalized classes. However, as new classes are loaded
|
|
// and finalized, there could be a new subtype of [cls]
|
|
// which is also a subtype of Future.
|
|
// We should deoptimize in such cases.
|
|
Thread* thread = Thread::Current();
|
|
if (FLAG_use_cha_deopt || thread->isolate_group()->all_classes_finalized()) {
|
|
if (FLAG_use_cha_deopt) {
|
|
CHA& cha = thread->compiler_state().cha();
|
|
cha.AddToGuardedClassesToTrackFuture(cls);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Conservatively assume that class can be Future.
|
|
return true;
|
|
}
|
|
|
|
static intptr_t CountFinalizedSubclasses(Thread* thread, const Class& cls) {
|
|
intptr_t count = 0;
|
|
const GrowableObjectArray& cls_direct_subclasses =
|
|
GrowableObjectArray::Handle(thread->zone(), cls.direct_subclasses());
|
|
if (cls_direct_subclasses.IsNull()) return count;
|
|
Class& direct_subclass = Class::Handle(thread->zone());
|
|
for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) {
|
|
direct_subclass ^= cls_direct_subclasses.At(i);
|
|
// Unfinalized classes are treated as nonexistent for CHA purposes,
|
|
// as that means that no instance of that class exists at runtime.
|
|
if (!direct_subclass.is_finalized()) {
|
|
continue;
|
|
}
|
|
|
|
count += 1 + CountFinalizedSubclasses(thread, direct_subclass);
|
|
}
|
|
return count;
|
|
}
|
|
|
|
bool CHA::IsConsistentWithCurrentHierarchy() const {
|
|
for (intptr_t i = 0; i < guarded_classes_.length(); i++) {
|
|
if (guarded_classes_[i].subclass_count != -1) {
|
|
intptr_t current_subclass_count =
|
|
CountFinalizedSubclasses(thread_, *guarded_classes_[i].cls);
|
|
if (guarded_classes_[i].subclass_count != current_subclass_count) {
|
|
return false; // New subclass appeared during compilation.
|
|
}
|
|
}
|
|
if (guarded_classes_[i].implementor_cid != kIllegalCid) {
|
|
intptr_t current_implementor_cid =
|
|
guarded_classes_[i].cls->implementor_cid();
|
|
if (guarded_classes_[i].implementor_cid != current_implementor_cid) {
|
|
return false; // New implementor appeared during compilation.
|
|
}
|
|
}
|
|
if (guarded_classes_[i].track_future) {
|
|
if (guarded_classes_[i].cls->can_be_future()) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CHA::HasOverride(const Class& cls,
|
|
const String& function_name,
|
|
intptr_t* subclasses_count) {
|
|
// Can't track dependencies for classes on the VM heap since those are
|
|
// read-only.
|
|
// TODO(fschneider): Enable tracking of CHA dependent code for VM heap
|
|
// classes.
|
|
if (cls.InVMIsolateHeap()) return true;
|
|
|
|
// Subclasses of Object are not tracked by CHA. Safely assume that overrides
|
|
// exist.
|
|
if (cls.IsObjectClass()) {
|
|
return true;
|
|
}
|
|
|
|
SafepointReadRwLocker ml(thread_, thread_->isolate_group()->program_lock());
|
|
const GrowableObjectArray& cls_direct_subclasses =
|
|
GrowableObjectArray::Handle(thread_->zone(), cls.direct_subclasses());
|
|
if (cls_direct_subclasses.IsNull()) {
|
|
return false;
|
|
}
|
|
Class& direct_subclass = Class::Handle(thread_->zone());
|
|
for (intptr_t i = 0; i < cls_direct_subclasses.Length(); i++) {
|
|
direct_subclass ^= cls_direct_subclasses.At(i);
|
|
// Unfinalized classes are treated as nonexistent for CHA purposes,
|
|
// as that means that no instance of that class exists at runtime.
|
|
if (!direct_subclass.is_finalized()) {
|
|
continue;
|
|
}
|
|
|
|
if (direct_subclass.LookupDynamicFunctionUnsafe(function_name) !=
|
|
Function::null()) {
|
|
return true;
|
|
}
|
|
|
|
if (HasOverride(direct_subclass, function_name, subclasses_count)) {
|
|
return true;
|
|
}
|
|
|
|
(*subclasses_count)++;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CHA::RegisterDependencies(const Code& code) const {
|
|
for (intptr_t i = 0; i < guarded_classes_.length(); ++i) {
|
|
guarded_classes_[i].cls->RegisterCHACode(code);
|
|
}
|
|
}
|
|
|
|
} // namespace dart
|