f205292227
This is the final CL which adds a new --use-bare-instructions flag to
the VM.
If this flag is set during AOT compilation, we will:
* Build one global object pool (abbr: GOP) which all code objects
share. This gop will be stored in the object store. The PP register
is populated in the enter dart stub and it is restored when
returning from native calls.
* Gets rid of the CODE_REG/PP slots from the dart frames. Instead the
compiled code uses the global object pool, which is always in PP.
* Starts emitting pc-relative calls for calls between two dart
functions or when invoking a stub.
Limitation: We only emit pc-relative calls between two code objects
in the same isolate (this is because the image writer is writing
instruction objects for vm-isolate/main-isolate seperately)
* We do compile-time relocation of those static calls after the
precompiler has finished its work, but before writing the snapshot.
This patches all the instruction objects with pc-relative calls to
have the right .text distance.
* We emit a sorted list of code objects in ObjectStore::reverse_code_table,
which will be used by the AOT runtime to go back from PC to Code
objects (where all metadata, e.g. stack maps, catch entry moves, pc
descriptors are available).
Issue https://github.com/dart-lang/sdk/issues/33274
Change-Id: I6c5dd2b1571e3a889b27e804a24c2986c71e03b6
Reviewed-on: https://dart-review.googlesource.com/c/85769
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
847 lines
26 KiB
C++
847 lines
26 KiB
C++
// Copyright (c) 2015, 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/program_visitor.h"
|
|
|
|
#include "vm/deopt_instructions.h"
|
|
#include "vm/hash_map.h"
|
|
#include "vm/object.h"
|
|
#include "vm/object_store.h"
|
|
#include "vm/symbols.h"
|
|
|
|
namespace dart {
|
|
|
|
void ProgramVisitor::VisitClasses(ClassVisitor* visitor) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
Zone* zone = thread->zone();
|
|
GrowableObjectArray& libraries =
|
|
GrowableObjectArray::Handle(zone, isolate->object_store()->libraries());
|
|
Library& lib = Library::Handle(zone);
|
|
Class& cls = Class::Handle(zone);
|
|
Object& entry = Object::Handle(zone);
|
|
GrowableObjectArray& patches = GrowableObjectArray::Handle(zone);
|
|
|
|
for (intptr_t i = 0; i < libraries.Length(); i++) {
|
|
lib ^= libraries.At(i);
|
|
ClassDictionaryIterator it(lib, ClassDictionaryIterator::kIteratePrivate);
|
|
while (it.HasNext()) {
|
|
cls = it.GetNextClass();
|
|
if (cls.IsDynamicClass()) {
|
|
continue; // class 'dynamic' is in the read-only VM isolate.
|
|
}
|
|
visitor->Visit(cls);
|
|
}
|
|
patches = lib.patch_classes();
|
|
for (intptr_t j = 0; j < patches.Length(); j++) {
|
|
entry = patches.At(j);
|
|
if (entry.IsClass()) {
|
|
visitor->Visit(Class::Cast(entry));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class ClassFunctionVisitor : public ClassVisitor {
|
|
public:
|
|
ClassFunctionVisitor(Zone* zone, FunctionVisitor* visitor)
|
|
: visitor_(visitor),
|
|
functions_(Array::Handle(zone)),
|
|
function_(Function::Handle(zone)),
|
|
object_(Object::Handle(zone)),
|
|
fields_(Array::Handle(zone)),
|
|
field_(Field::Handle(zone)) {}
|
|
|
|
void Visit(const Class& cls) {
|
|
if (cls.IsDynamicClass()) {
|
|
return; // class 'dynamic' is in the read-only VM isolate.
|
|
}
|
|
|
|
functions_ = cls.functions();
|
|
for (intptr_t j = 0; j < functions_.Length(); j++) {
|
|
function_ ^= functions_.At(j);
|
|
visitor_->Visit(function_);
|
|
if (function_.HasImplicitClosureFunction()) {
|
|
function_ = function_.ImplicitClosureFunction();
|
|
visitor_->Visit(function_);
|
|
}
|
|
}
|
|
|
|
functions_ = cls.invocation_dispatcher_cache();
|
|
for (intptr_t j = 0; j < functions_.Length(); j++) {
|
|
object_ = functions_.At(j);
|
|
if (object_.IsFunction()) {
|
|
function_ ^= functions_.At(j);
|
|
visitor_->Visit(function_);
|
|
}
|
|
}
|
|
|
|
fields_ = cls.fields();
|
|
for (intptr_t j = 0; j < fields_.Length(); j++) {
|
|
field_ ^= fields_.At(j);
|
|
if (field_.is_static() && field_.HasPrecompiledInitializer()) {
|
|
function_ ^= field_.PrecompiledInitializer();
|
|
visitor_->Visit(function_);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
FunctionVisitor* visitor_;
|
|
Array& functions_;
|
|
Function& function_;
|
|
Object& object_;
|
|
Array& fields_;
|
|
Field& field_;
|
|
};
|
|
|
|
void ProgramVisitor::VisitFunctions(FunctionVisitor* visitor) {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
Zone* zone = thread->zone();
|
|
|
|
ClassFunctionVisitor class_visitor(zone, visitor);
|
|
VisitClasses(&class_visitor);
|
|
|
|
Function& function = Function::Handle(zone);
|
|
const GrowableObjectArray& closures = GrowableObjectArray::Handle(
|
|
zone, isolate->object_store()->closure_functions());
|
|
for (intptr_t i = 0; i < closures.Length(); i++) {
|
|
function ^= closures.At(i);
|
|
visitor->Visit(function);
|
|
ASSERT(!function.HasImplicitClosureFunction());
|
|
}
|
|
}
|
|
|
|
void ProgramVisitor::ShareMegamorphicBuckets() {
|
|
Thread* thread = Thread::Current();
|
|
Isolate* isolate = thread->isolate();
|
|
Zone* zone = thread->zone();
|
|
|
|
const GrowableObjectArray& table = GrowableObjectArray::Handle(
|
|
zone, isolate->object_store()->megamorphic_cache_table());
|
|
if (table.IsNull()) return;
|
|
MegamorphicCache& cache = MegamorphicCache::Handle(zone);
|
|
|
|
const intptr_t capacity = 1;
|
|
const Array& buckets = Array::Handle(
|
|
zone, Array::New(MegamorphicCache::kEntryLength * capacity, Heap::kOld));
|
|
const Function& handler =
|
|
Function::Handle(zone, MegamorphicCacheTable::miss_handler(isolate));
|
|
MegamorphicCache::SetEntry(buckets, 0, Object::smi_illegal_cid(), handler);
|
|
|
|
for (intptr_t i = 0; i < table.Length(); i++) {
|
|
cache ^= table.At(i);
|
|
cache.set_buckets(buckets);
|
|
cache.set_mask(capacity - 1);
|
|
cache.set_filled_entry_count(0);
|
|
}
|
|
}
|
|
|
|
class StackMapKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const StackMap* Key;
|
|
typedef const StackMap* Value;
|
|
typedef const StackMap* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->PcOffset(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->Equals(*key);
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<StackMapKeyValueTrait> StackMapSet;
|
|
|
|
void ProgramVisitor::DedupStackMaps() {
|
|
class DedupStackMapsVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupStackMapsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_stackmaps_(),
|
|
code_(Code::Handle(zone)),
|
|
stackmaps_(Array::Handle(zone)),
|
|
stackmap_(StackMap::Handle(zone)) {}
|
|
|
|
void AddStackMap(const StackMap& stackmap) {
|
|
canonical_stackmaps_.Insert(&StackMap::ZoneHandle(zone_, stackmap.raw()));
|
|
}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
stackmaps_ = code_.stackmaps();
|
|
if (stackmaps_.IsNull()) return;
|
|
for (intptr_t i = 0; i < stackmaps_.Length(); i++) {
|
|
stackmap_ ^= stackmaps_.At(i);
|
|
stackmap_ = DedupStackMap(stackmap_);
|
|
stackmaps_.SetAt(i, stackmap_);
|
|
}
|
|
}
|
|
|
|
RawStackMap* DedupStackMap(const StackMap& stackmap) {
|
|
const StackMap* canonical_stackmap =
|
|
canonical_stackmaps_.LookupValue(&stackmap);
|
|
if (canonical_stackmap == NULL) {
|
|
AddStackMap(stackmap);
|
|
return stackmap.raw();
|
|
} else {
|
|
return canonical_stackmap->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
StackMapSet canonical_stackmaps_;
|
|
Code& code_;
|
|
Array& stackmaps_;
|
|
StackMap& stackmap_;
|
|
};
|
|
|
|
DedupStackMapsVisitor visitor(Thread::Current()->zone());
|
|
if (Snapshot::IncludesCode(Dart::vm_snapshot_kind())) {
|
|
// Prefer existing objects in the VM isolate.
|
|
const Array& object_table = Object::vm_isolate_snapshot_object_table();
|
|
Object& object = Object::Handle();
|
|
for (intptr_t i = 0; i < object_table.Length(); i++) {
|
|
object = object_table.At(i);
|
|
if (object.IsStackMap()) {
|
|
visitor.AddStackMap(StackMap::Cast(object));
|
|
}
|
|
}
|
|
}
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
|
|
class PcDescriptorsKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const PcDescriptors* Key;
|
|
typedef const PcDescriptors* Value;
|
|
typedef const PcDescriptors* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->Length(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->Equals(*key);
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<PcDescriptorsKeyValueTrait> PcDescriptorsSet;
|
|
|
|
void ProgramVisitor::DedupPcDescriptors() {
|
|
class DedupPcDescriptorsVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupPcDescriptorsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_pc_descriptors_(),
|
|
code_(Code::Handle(zone)),
|
|
pc_descriptor_(PcDescriptors::Handle(zone)) {}
|
|
|
|
void AddPcDescriptor(const PcDescriptors& pc_descriptor) {
|
|
canonical_pc_descriptors_.Insert(
|
|
&PcDescriptors::ZoneHandle(zone_, pc_descriptor.raw()));
|
|
}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
pc_descriptor_ = code_.pc_descriptors();
|
|
if (pc_descriptor_.IsNull()) return;
|
|
pc_descriptor_ = DedupPcDescriptor(pc_descriptor_);
|
|
code_.set_pc_descriptors(pc_descriptor_);
|
|
}
|
|
|
|
RawPcDescriptors* DedupPcDescriptor(const PcDescriptors& pc_descriptor) {
|
|
const PcDescriptors* canonical_pc_descriptor =
|
|
canonical_pc_descriptors_.LookupValue(&pc_descriptor);
|
|
if (canonical_pc_descriptor == NULL) {
|
|
AddPcDescriptor(pc_descriptor);
|
|
return pc_descriptor.raw();
|
|
} else {
|
|
return canonical_pc_descriptor->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
PcDescriptorsSet canonical_pc_descriptors_;
|
|
Code& code_;
|
|
PcDescriptors& pc_descriptor_;
|
|
};
|
|
|
|
DedupPcDescriptorsVisitor visitor(Thread::Current()->zone());
|
|
if (Snapshot::IncludesCode(Dart::vm_snapshot_kind())) {
|
|
// Prefer existing objects in the VM isolate.
|
|
const Array& object_table = Object::vm_isolate_snapshot_object_table();
|
|
Object& object = Object::Handle();
|
|
for (intptr_t i = 0; i < object_table.Length(); i++) {
|
|
object = object_table.At(i);
|
|
if (object.IsPcDescriptors()) {
|
|
visitor.AddPcDescriptor(PcDescriptors::Cast(object));
|
|
}
|
|
}
|
|
}
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
|
|
class TypedDataKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const TypedData* Key;
|
|
typedef const TypedData* Value;
|
|
typedef const TypedData* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->CanonicalizeHash(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->CanonicalizeEquals(*key);
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<TypedDataKeyValueTrait> TypedDataSet;
|
|
|
|
#if !defined(DART_PRECOMPILED_RUNTIME)
|
|
void ProgramVisitor::DedupDeoptEntries() {
|
|
class DedupDeoptEntriesVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupDeoptEntriesVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_deopt_entries_(),
|
|
code_(Code::Handle(zone)),
|
|
deopt_table_(Array::Handle(zone)),
|
|
deopt_entry_(TypedData::Handle(zone)),
|
|
offset_(Smi::Handle(zone)),
|
|
reason_and_flags_(Smi::Handle(zone)) {}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
deopt_table_ = code_.deopt_info_array();
|
|
if (deopt_table_.IsNull()) return;
|
|
intptr_t length = DeoptTable::GetLength(deopt_table_);
|
|
for (intptr_t i = 0; i < length; i++) {
|
|
DeoptTable::GetEntry(deopt_table_, i, &offset_, &deopt_entry_,
|
|
&reason_and_flags_);
|
|
ASSERT(!deopt_entry_.IsNull());
|
|
deopt_entry_ = DedupDeoptEntry(deopt_entry_);
|
|
ASSERT(!deopt_entry_.IsNull());
|
|
DeoptTable::SetEntry(deopt_table_, i, offset_, deopt_entry_,
|
|
reason_and_flags_);
|
|
}
|
|
}
|
|
|
|
RawTypedData* DedupDeoptEntry(const TypedData& deopt_entry) {
|
|
const TypedData* canonical_deopt_entry =
|
|
canonical_deopt_entries_.LookupValue(&deopt_entry);
|
|
if (canonical_deopt_entry == NULL) {
|
|
canonical_deopt_entries_.Insert(
|
|
&TypedData::ZoneHandle(zone_, deopt_entry.raw()));
|
|
return deopt_entry.raw();
|
|
} else {
|
|
return canonical_deopt_entry->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
TypedDataSet canonical_deopt_entries_;
|
|
Code& code_;
|
|
Array& deopt_table_;
|
|
TypedData& deopt_entry_;
|
|
Smi& offset_;
|
|
Smi& reason_and_flags_;
|
|
};
|
|
|
|
DedupDeoptEntriesVisitor visitor(Thread::Current()->zone());
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
void ProgramVisitor::DedupCatchEntryMovesMaps() {
|
|
if (!FLAG_precompiled_mode) {
|
|
return;
|
|
}
|
|
class DedupCatchEntryMovesMapsVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupCatchEntryMovesMapsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_catch_entry_moves_maps_(),
|
|
code_(Code::Handle(zone)),
|
|
catch_entry_moves_maps_(TypedData::Handle(zone)) {}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
catch_entry_moves_maps_ = code_.catch_entry_moves_maps();
|
|
catch_entry_moves_maps_ =
|
|
DedupCatchEntryMovesMaps(catch_entry_moves_maps_);
|
|
code_.set_catch_entry_moves_maps(catch_entry_moves_maps_);
|
|
}
|
|
|
|
RawTypedData* DedupCatchEntryMovesMaps(
|
|
const TypedData& catch_entry_moves_maps) {
|
|
const TypedData* canonical_catch_entry_moves_maps =
|
|
canonical_catch_entry_moves_maps_.LookupValue(
|
|
&catch_entry_moves_maps);
|
|
if (canonical_catch_entry_moves_maps == NULL) {
|
|
canonical_catch_entry_moves_maps_.Insert(
|
|
&TypedData::ZoneHandle(zone_, catch_entry_moves_maps.raw()));
|
|
return catch_entry_moves_maps.raw();
|
|
} else {
|
|
return canonical_catch_entry_moves_maps->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
TypedDataSet canonical_catch_entry_moves_maps_;
|
|
Code& code_;
|
|
TypedData& catch_entry_moves_maps_;
|
|
};
|
|
|
|
DedupCatchEntryMovesMapsVisitor visitor(Thread::Current()->zone());
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
#endif // !defined(DART_PRECOMPILER)
|
|
|
|
class CodeSourceMapKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const CodeSourceMap* Key;
|
|
typedef const CodeSourceMap* Value;
|
|
typedef const CodeSourceMap* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->Length(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->Equals(*key);
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<CodeSourceMapKeyValueTrait> CodeSourceMapSet;
|
|
|
|
void ProgramVisitor::DedupCodeSourceMaps() {
|
|
class DedupCodeSourceMapsVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupCodeSourceMapsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_code_source_maps_(),
|
|
code_(Code::Handle(zone)),
|
|
code_source_map_(CodeSourceMap::Handle(zone)) {}
|
|
|
|
void AddCodeSourceMap(const CodeSourceMap& code_source_map) {
|
|
canonical_code_source_maps_.Insert(
|
|
&CodeSourceMap::ZoneHandle(zone_, code_source_map.raw()));
|
|
}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
code_source_map_ = code_.code_source_map();
|
|
ASSERT(!code_source_map_.IsNull());
|
|
code_source_map_ = DedupCodeSourceMap(code_source_map_);
|
|
code_.set_code_source_map(code_source_map_);
|
|
}
|
|
|
|
RawCodeSourceMap* DedupCodeSourceMap(const CodeSourceMap& code_source_map) {
|
|
const CodeSourceMap* canonical_code_source_map =
|
|
canonical_code_source_maps_.LookupValue(&code_source_map);
|
|
if (canonical_code_source_map == NULL) {
|
|
AddCodeSourceMap(code_source_map);
|
|
return code_source_map.raw();
|
|
} else {
|
|
return canonical_code_source_map->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
CodeSourceMapSet canonical_code_source_maps_;
|
|
Code& code_;
|
|
CodeSourceMap& code_source_map_;
|
|
};
|
|
|
|
DedupCodeSourceMapsVisitor visitor(Thread::Current()->zone());
|
|
if (Snapshot::IncludesCode(Dart::vm_snapshot_kind())) {
|
|
// Prefer existing objects in the VM isolate.
|
|
const Array& object_table = Object::vm_isolate_snapshot_object_table();
|
|
Object& object = Object::Handle();
|
|
for (intptr_t i = 0; i < object_table.Length(); i++) {
|
|
object = object_table.At(i);
|
|
if (object.IsCodeSourceMap()) {
|
|
visitor.AddCodeSourceMap(CodeSourceMap::Cast(object));
|
|
}
|
|
}
|
|
}
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
|
|
class ArrayKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const Array* Key;
|
|
typedef const Array* Value;
|
|
typedef const Array* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->Length(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
if (pair->Length() != key->Length()) {
|
|
return false;
|
|
}
|
|
for (intptr_t i = 0; i < pair->Length(); i++) {
|
|
if (pair->At(i) != key->At(i)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<ArrayKeyValueTrait> ArraySet;
|
|
|
|
void ProgramVisitor::DedupLists() {
|
|
class DedupListsVisitor : public FunctionVisitor {
|
|
public:
|
|
explicit DedupListsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_lists_(),
|
|
code_(Code::Handle(zone)),
|
|
list_(Array::Handle(zone)) {}
|
|
|
|
void Visit(const Function& function) {
|
|
code_ = function.CurrentCode();
|
|
if (!code_.IsNull()) {
|
|
list_ = code_.stackmaps();
|
|
if (!list_.IsNull()) {
|
|
list_ = DedupList(list_);
|
|
code_.set_stackmaps(list_);
|
|
}
|
|
list_ = code_.inlined_id_to_function();
|
|
if (!list_.IsNull()) {
|
|
list_ = DedupList(list_);
|
|
code_.set_inlined_id_to_function(list_);
|
|
}
|
|
list_ = code_.deopt_info_array();
|
|
if (!list_.IsNull()) {
|
|
list_ = DedupList(list_);
|
|
code_.set_deopt_info_array(list_);
|
|
}
|
|
#ifndef PRODUCT
|
|
list_ = code_.await_token_positions();
|
|
if (!list_.IsNull()) {
|
|
list_ = DedupList(list_);
|
|
code_.set_await_token_positions(list_);
|
|
}
|
|
#endif // !PRODUCT
|
|
list_ = code_.static_calls_target_table();
|
|
if (!list_.IsNull()) {
|
|
list_ = DedupList(list_);
|
|
code_.set_static_calls_target_table(list_);
|
|
}
|
|
}
|
|
|
|
list_ = function.parameter_types();
|
|
if (!list_.IsNull()) {
|
|
// Preserve parameter types in the JIT. Needed in case of recompilation
|
|
// in checked mode, or if available to mirrors, or for copied types to
|
|
// lazily generated tear offs.
|
|
if (FLAG_precompiled_mode) {
|
|
if (!function.IsSignatureFunction() &&
|
|
!function.IsClosureFunction() &&
|
|
(function.name() != Symbols::Call().raw()) && !list_.InVMHeap()) {
|
|
// Parameter types not needed for function type tests.
|
|
for (intptr_t i = 0; i < list_.Length(); i++) {
|
|
list_.SetAt(i, Object::dynamic_type());
|
|
}
|
|
}
|
|
}
|
|
list_ = DedupList(list_);
|
|
function.set_parameter_types(list_);
|
|
}
|
|
|
|
list_ = function.parameter_names();
|
|
if (!list_.IsNull()) {
|
|
// Preserve parameter names in case of recompilation for the JIT.
|
|
if (FLAG_precompiled_mode) {
|
|
if (!function.HasOptionalNamedParameters() && !list_.InVMHeap()) {
|
|
// Parameter names not needed for resolution.
|
|
for (intptr_t i = 0; i < list_.Length(); i++) {
|
|
list_.SetAt(i, Symbols::OptimizedOut());
|
|
}
|
|
}
|
|
}
|
|
list_ = DedupList(list_);
|
|
function.set_parameter_names(list_);
|
|
}
|
|
}
|
|
|
|
RawArray* DedupList(const Array& list) {
|
|
if (list.InVMHeap()) {
|
|
// Avoid using read-only VM objects for de-duplication.
|
|
return list.raw();
|
|
}
|
|
const Array* canonical_list = canonical_lists_.LookupValue(&list);
|
|
if (canonical_list == NULL) {
|
|
canonical_lists_.Insert(&Array::ZoneHandle(zone_, list.raw()));
|
|
return list.raw();
|
|
} else {
|
|
return canonical_list->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
ArraySet canonical_lists_;
|
|
Code& code_;
|
|
Array& list_;
|
|
};
|
|
|
|
DedupListsVisitor visitor(Thread::Current()->zone());
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
|
|
// Traits for comparing two [Instructions] objects for equality, which is
|
|
// implemented as bit-wise equality.
|
|
//
|
|
// This considers two instruction objects to be equal even if they have
|
|
// different static call targets. Since the static call targets are called via
|
|
// the object pool this is ok.
|
|
class InstructionsKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const Instructions* Key;
|
|
typedef const Instructions* Value;
|
|
typedef const Instructions* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->Size(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
return pair->Equals(*key);
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<InstructionsKeyValueTrait> InstructionsSet;
|
|
|
|
// Traits for comparing two [Code] objects for equality.
|
|
//
|
|
// It considers two [Code] objects to be equal if
|
|
//
|
|
// * their [RawInstruction]s are bit-wise equal
|
|
// * their [RawPcDescriptor]s are the same
|
|
// * their [RawStackMaps]s are the same
|
|
// * their static call targets are the same
|
|
#if defined(DART_PRECOMPILER)
|
|
class CodeKeyValueTrait {
|
|
public:
|
|
// Typedefs needed for the DirectChainedHashMap template.
|
|
typedef const Code* Key;
|
|
typedef const Code* Value;
|
|
typedef const Code* Pair;
|
|
|
|
static Key KeyOf(Pair kv) { return kv; }
|
|
|
|
static Value ValueOf(Pair kv) { return kv; }
|
|
|
|
static inline intptr_t Hashcode(Key key) { return key->Size(); }
|
|
|
|
static inline bool IsKeyEqual(Pair pair, Key key) {
|
|
if (pair->raw() == key->raw()) return true;
|
|
|
|
// Notice we assume that these entries have already been de-duped, so we
|
|
// can use pointer equality.
|
|
if (pair->static_calls_target_table() != key->static_calls_target_table()) {
|
|
return false;
|
|
}
|
|
if (pair->pc_descriptors() == key->pc_descriptors()) {
|
|
return false;
|
|
}
|
|
if (pair->stackmaps() == key->stackmaps()) {
|
|
return false;
|
|
}
|
|
if (pair->catch_entry_moves_maps() == key->catch_entry_moves_maps()) {
|
|
return false;
|
|
}
|
|
return Instructions::Equals(pair->instructions(), key->instructions());
|
|
}
|
|
};
|
|
|
|
typedef DirectChainedHashMap<CodeKeyValueTrait> CodeSet;
|
|
#endif // defined(DART_PRECOMPILER)
|
|
|
|
void ProgramVisitor::DedupInstructions() {
|
|
class DedupInstructionsVisitor : public FunctionVisitor,
|
|
public ObjectVisitor {
|
|
public:
|
|
explicit DedupInstructionsVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_instructions_set_(),
|
|
code_(Code::Handle(zone)),
|
|
instructions_(Instructions::Handle(zone)) {}
|
|
|
|
void VisitObject(RawObject* obj) {
|
|
if (obj->IsInstructions()) {
|
|
canonical_instructions_set_.Insert(
|
|
&Instructions::ZoneHandle(zone_, Instructions::RawCast(obj)));
|
|
}
|
|
}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
instructions_ = code_.instructions();
|
|
instructions_ = DedupOneInstructions(instructions_);
|
|
code_.SetActiveInstructions(instructions_);
|
|
code_.set_instructions(instructions_);
|
|
function.SetInstructions(code_); // Update cached entry point.
|
|
}
|
|
|
|
RawInstructions* DedupOneInstructions(const Instructions& instructions) {
|
|
const Instructions* canonical_instructions =
|
|
canonical_instructions_set_.LookupValue(&instructions);
|
|
if (canonical_instructions == NULL) {
|
|
canonical_instructions_set_.Insert(
|
|
&Instructions::ZoneHandle(zone_, instructions.raw()));
|
|
return instructions.raw();
|
|
} else {
|
|
return canonical_instructions->raw();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
InstructionsSet canonical_instructions_set_;
|
|
Code& code_;
|
|
Instructions& instructions_;
|
|
};
|
|
|
|
DedupInstructionsVisitor visitor(Thread::Current()->zone());
|
|
if (Snapshot::IncludesCode(Dart::vm_snapshot_kind())) {
|
|
// Prefer existing objects in the VM isolate.
|
|
Dart::vm_isolate()->heap()->VisitObjectsImagePages(&visitor);
|
|
}
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
}
|
|
|
|
void ProgramVisitor::DedupInstructionsWithSameMetadata() {
|
|
#if defined(DART_PRECOMPILER)
|
|
class DedupInstructionsWithSameMetadataVisitor : public FunctionVisitor,
|
|
public ObjectVisitor {
|
|
public:
|
|
explicit DedupInstructionsWithSameMetadataVisitor(Zone* zone)
|
|
: zone_(zone),
|
|
canonical_set_(),
|
|
code_(Code::Handle(zone)),
|
|
owner_(Object::Handle(zone)),
|
|
instructions_(Instructions::Handle(zone)) {}
|
|
|
|
void VisitObject(RawObject* obj) {
|
|
if (obj->IsCode()) {
|
|
canonical_set_.Insert(&Code::ZoneHandle(zone_, Code::RawCast(obj)));
|
|
}
|
|
}
|
|
|
|
void Visit(const Function& function) {
|
|
if (!function.HasCode()) {
|
|
return;
|
|
}
|
|
code_ = function.CurrentCode();
|
|
instructions_ = DedupOneInstructions(code_);
|
|
code_.SetActiveInstructions(instructions_);
|
|
code_.set_instructions(instructions_);
|
|
function.SetInstructions(code_); // Update cached entry point.
|
|
}
|
|
|
|
RawInstructions* DedupOneInstructions(const Code& code) {
|
|
const Code* canonical = canonical_set_.LookupValue(&code);
|
|
if (canonical == NULL) {
|
|
canonical_set_.Insert(&Code::ZoneHandle(zone_, code.raw()));
|
|
return code.instructions();
|
|
} else {
|
|
owner_ = code.owner();
|
|
return canonical->instructions();
|
|
}
|
|
}
|
|
|
|
private:
|
|
Zone* zone_;
|
|
CodeSet canonical_set_;
|
|
Code& code_;
|
|
Object& owner_;
|
|
Instructions& instructions_;
|
|
};
|
|
|
|
DedupInstructionsWithSameMetadataVisitor visitor(Thread::Current()->zone());
|
|
ProgramVisitor::VisitFunctions(&visitor);
|
|
#endif // defined(DART_PRECOMPILER)
|
|
}
|
|
|
|
void ProgramVisitor::Dedup() {
|
|
Thread* thread = Thread::Current();
|
|
StackZone stack_zone(thread);
|
|
HANDLESCOPE(thread);
|
|
|
|
// TODO(rmacnak): Bind static calls whose target has been compiled. Forward
|
|
// references to disabled code.
|
|
ShareMegamorphicBuckets();
|
|
DedupStackMaps();
|
|
DedupPcDescriptors();
|
|
NOT_IN_PRECOMPILED(DedupDeoptEntries());
|
|
#if defined(DART_PRECOMPILER)
|
|
DedupCatchEntryMovesMaps();
|
|
#endif
|
|
DedupCodeSourceMaps();
|
|
DedupLists();
|
|
|
|
#if defined(PRODUCT)
|
|
// Reduces binary size but obfuscates profiler results.
|
|
if (FLAG_precompiled_mode && FLAG_use_bare_instructions) {
|
|
DedupInstructionsWithSameMetadata();
|
|
} else {
|
|
DedupInstructions();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
} // namespace dart
|