Index: runtime/vm/program_visitor.cc |
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc |
index 4a41925b7f74bd9abcabaa6b80478ebf18f876f8..cfeb9a41262d9ddc596a4572e8093ebe9c0d3afd 100644 |
--- a/runtime/vm/program_visitor.cc |
+++ b/runtime/vm/program_visitor.cc |
@@ -4,8 +4,11 @@ |
#include "vm/program_visitor.h" |
+#include "vm/deopt_instructions.h" |
#include "vm/object.h" |
#include "vm/object_store.h" |
+#include "vm/hash_map.h" |
+#include "vm/symbols.h" |
namespace dart { |
@@ -92,4 +95,530 @@ void ProgramVisitor::VisitFunctions(FunctionVisitor* visitor) { |
} |
} |
+ |
+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, MegamorphicCache::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 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) { |
+ canonical_stackmaps_.Insert( |
+ &StackMap::ZoneHandle(zone_, stackmap.raw())); |
+ 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()); |
+ 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 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) { |
+ canonical_pc_descriptors_.Insert( |
+ &PcDescriptors::ZoneHandle(zone_, pc_descriptor.raw())); |
+ 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()); |
+ 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->ComputeCanonicalTableHash(); |
+ } |
+ |
+ static inline bool IsKeyEqual(Pair pair, Key key) { |
+ return pair->CanonicalizeEquals(*key); |
+ } |
+}; |
+ |
+typedef DirectChainedHashMap<TypedDataKeyValueTrait> TypedDataSet; |
+ |
+ |
+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); |
+} |
+ |
+ |
+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 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) { |
+ canonical_code_source_maps_.Insert( |
+ &CodeSourceMap::ZoneHandle(zone_, code_source_map.raw())); |
+ 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()); |
+ 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: |
+ DedupListsVisitor(Isolate* isolate, Zone* zone) |
+ : isolate_(isolate), |
+ 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 case of recompilation in JIT checked |
+ // mode, or if available to mirrors. |
+ if (FLAG_precompiled_mode || |
+ (!FLAG_enable_mirrors && !isolate_->type_checks())) { |
+ 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) { |
+ 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: |
+ Isolate* isolate_; |
+ Zone* zone_; |
+ ArraySet canonical_lists_; |
+ Code& code_; |
+ Array& list_; |
+ }; |
+ |
+ Thread* thread = Thread::Current(); |
+ DedupListsVisitor visitor(thread->isolate(), thread->zone()); |
+ ProgramVisitor::VisitFunctions(&visitor); |
+} |
+ |
+ |
+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; |
+ |
+ |
+void ProgramVisitor::DedupInstructions() { |
+ class DedupInstructionsVisitor : public FunctionVisitor { |
+ public: |
+ explicit DedupInstructionsVisitor(Zone* zone) |
+ : zone_(zone), |
+ canonical_instructions_set_(), |
+ code_(Code::Handle(zone)), |
+ instructions_(Instructions::Handle(zone)) {} |
+ |
+ void Visit(const Function& function) { |
+ if (!function.HasCode()) { |
+ ASSERT(function.HasImplicitClosureFunction()); |
+ 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()); |
+ ProgramVisitor::VisitFunctions(&visitor); |
+} |
+ |
+ |
+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(); |
+ DedupDeoptEntries(); |
+ DedupCodeSourceMaps(); |
+ DedupLists(); |
+ |
+ if (!FLAG_profiler) { |
+ // Reduces binary size but obfuscates profiler results. |
+ DedupInstructions(); |
+ } |
+} |
+ |
} // namespace dart |