| // 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. |
| |
| #include "vm/compiler/backend/code_statistics.h" |
| |
| namespace dart { |
| |
| CombinedCodeStatistics::CombinedCodeStatistics() { |
| unaccounted_bytes_ = 0; |
| alignment_bytes_ = 0; |
| object_header_bytes_ = 0; |
| return_const_count_ = 0; |
| return_const_with_load_field_count_ = 0; |
| intptr_t i = 0; |
| |
| #define DO(type, attrs) \ |
| entries_[i].name = #type; \ |
| entries_[i].bytes = 0; \ |
| entries_[i++].count = 0; |
| |
| FOR_EACH_INSTRUCTION(DO) |
| |
| #undef DO |
| |
| #define DO(type, attrs) \ |
| entries_[i].name = "SlowPath:" #type; \ |
| entries_[i].bytes = 0; \ |
| entries_[i++].count = 0; |
| |
| FOR_EACH_INSTRUCTION(DO) |
| |
| #undef DO |
| |
| #define INIT_SPECIAL_ENTRY(tag, str) \ |
| entries_[tag].name = str; \ |
| entries_[tag].bytes = 0; \ |
| entries_[tag].count = 0; |
| |
| INIT_SPECIAL_ENTRY(kTagAssertAssignableParameterCheck, |
| "AssertAssignable:ParameterCheck"); |
| INIT_SPECIAL_ENTRY(kTagAssertAssignableInsertedByFrontend, |
| "AssertAssignable:InsertedByFrontend"); |
| INIT_SPECIAL_ENTRY(kTagAssertAssignableFromSource, |
| "AssertAssignable:FromSource"); |
| |
| INIT_SPECIAL_ENTRY(kTagCheckedEntry, "<checked-entry-prologue>"); |
| INIT_SPECIAL_ENTRY(kTagIntrinsics, "<intrinsics>"); |
| #undef INIT_SPECIAL_ENTRY |
| } |
| |
| void CombinedCodeStatistics::DumpStatistics() { |
| ASSERT(unaccounted_bytes_ >= 0); |
| |
| Entry* sorted[kNumEntries]; |
| for (intptr_t i = 0; i < kNumEntries; i++) { |
| sorted[i] = &entries_[i]; |
| } |
| qsort(sorted, kNumEntries, sizeof(Entry*), &CompareEntries); |
| |
| intptr_t instruction_bytes = 0; |
| for (intptr_t i = 0; i < kNumEntries; i++) { |
| instruction_bytes += entries_[i].bytes; |
| } |
| intptr_t total = object_header_bytes_ + instruction_bytes + |
| unaccounted_bytes_ + alignment_bytes_; |
| float ftotal = static_cast<float>(total) / 100.0; |
| |
| OS::PrintErr("--------------------\n"); |
| |
| for (intptr_t i = 0; i < kNumEntries; i++) { |
| Entry* entry = sorted[i]; |
| const char* name = entry->name; |
| intptr_t bytes = entry->bytes; |
| intptr_t count = entry->count; |
| float percent = bytes / ftotal; |
| float avg = static_cast<float>(bytes) / count; |
| if (bytes > 0) { |
| OS::PrintErr( |
| "%5.2f %% " |
| "% 8" Pd |
| " bytes " |
| "% 8" Pd |
| " count " |
| "%8.2f avg bytes/entry " |
| "- %s\n", |
| percent, bytes, count, avg, name); |
| } |
| } |
| |
| OS::PrintErr("--------------------\n"); |
| |
| OS::PrintErr("%5.2f %% % 8" Pd " bytes unaccounted\n", |
| unaccounted_bytes_ / ftotal, unaccounted_bytes_); |
| OS::PrintErr("%5.2f %% % 8" Pd " bytes alignment\n", |
| alignment_bytes_ / ftotal, alignment_bytes_); |
| OS::PrintErr("%5.2f %% % 8" Pd " bytes instruction object header\n", |
| object_header_bytes_ / ftotal, object_header_bytes_); |
| OS::PrintErr("%5.2f %% % 8" Pd " bytes instructions\n", |
| instruction_bytes / ftotal, instruction_bytes); |
| OS::PrintErr("--------------------\n"); |
| OS::PrintErr("%5.2f %% % 8" Pd " bytes in total\n", total / ftotal, total); |
| OS::PrintErr("--------------------\n"); |
| OS::PrintErr("% 8" Pd " return-constant functions\n", return_const_count_); |
| OS::PrintErr("% 8" Pd " return-constant-with-load-field functions\n", |
| return_const_with_load_field_count_); |
| OS::PrintErr("--------------------\n"); |
| } |
| |
| int CombinedCodeStatistics::CompareEntries(const void* a, const void* b) { |
| const intptr_t a_size = (*static_cast<const Entry* const*>(a))->bytes; |
| const intptr_t b_size = (*static_cast<const Entry* const*>(b))->bytes; |
| if (a_size < b_size) { |
| return -1; |
| } else if (a_size > b_size) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| CodeStatistics::CodeStatistics(compiler::Assembler* assembler) |
| : assembler_(assembler) { |
| memset(entries_, 0, CombinedCodeStatistics::kNumEntries * sizeof(Entry)); |
| instruction_bytes_ = 0; |
| unaccounted_bytes_ = 0; |
| alignment_bytes_ = 0; |
| |
| stack_index_ = -1; |
| for (intptr_t i = 0; i < kStackSize; i++) |
| stack_[i] = -1; |
| } |
| |
| void CodeStatistics::Begin(Instruction* instruction) { |
| SpecialBegin(static_cast<intptr_t>(instruction->statistics_tag())); |
| } |
| |
| void CodeStatistics::End(Instruction* instruction) { |
| SpecialEnd(static_cast<intptr_t>(instruction->statistics_tag())); |
| } |
| |
| void CodeStatistics::SpecialBegin(intptr_t tag) { |
| stack_index_++; |
| RELEASE_ASSERT(stack_index_ < kStackSize); |
| RELEASE_ASSERT(stack_[stack_index_] == -1); |
| RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
| stack_[stack_index_] = assembler_->CodeSize(); |
| RELEASE_ASSERT(stack_[stack_index_] >= 0); |
| } |
| |
| void CodeStatistics::SpecialEnd(intptr_t tag) { |
| RELEASE_ASSERT(stack_index_ > 0 || stack_[stack_index_] >= 0); |
| RELEASE_ASSERT(tag < CombinedCodeStatistics::kNumEntries); |
| |
| intptr_t diff = assembler_->CodeSize() - stack_[stack_index_]; |
| RELEASE_ASSERT(diff >= 0); |
| RELEASE_ASSERT(entries_[tag].bytes >= 0); |
| RELEASE_ASSERT(entries_[tag].count >= 0); |
| entries_[tag].bytes += diff; |
| entries_[tag].count++; |
| instruction_bytes_ += diff; |
| stack_[stack_index_] = -1; |
| stack_index_--; |
| } |
| |
| void CodeStatistics::Finalize() { |
| intptr_t function_size = assembler_->CodeSize(); |
| unaccounted_bytes_ = function_size - instruction_bytes_; |
| ASSERT(unaccounted_bytes_ >= 0); |
| |
| const intptr_t unaligned_bytes = Instructions::HeaderSize() + function_size; |
| alignment_bytes_ = |
| Utils::RoundUp(unaligned_bytes, kObjectAlignment) - unaligned_bytes; |
| assembler_ = NULL; |
| } |
| |
| void CodeStatistics::AppendTo(CombinedCodeStatistics* stat) { |
| bool returns_constant = true; |
| bool returns_const_with_load_field_ = true; |
| |
| for (intptr_t i = 0; i < CombinedCodeStatistics::kNumEntries; i++) { |
| intptr_t bytes = entries_[i].bytes; |
| stat->entries_[i].count += entries_[i].count; |
| if (bytes > 0) { |
| stat->entries_[i].bytes += bytes; |
| if (i != CombinedCodeStatistics::kTagParallelMove && |
| i != CombinedCodeStatistics::kTagReturn && |
| i != CombinedCodeStatistics::kTagCheckStackOverflow && |
| i != CombinedCodeStatistics::kTagCheckStackOverflowSlowPath) { |
| returns_constant = false; |
| if (i != CombinedCodeStatistics::kTagLoadField && |
| i != CombinedCodeStatistics::kTagTargetEntry && |
| i != CombinedCodeStatistics::kTagJoinEntry) { |
| returns_const_with_load_field_ = false; |
| } |
| } |
| } |
| } |
| stat->unaccounted_bytes_ += unaccounted_bytes_; |
| ASSERT(stat->unaccounted_bytes_ >= 0); |
| stat->alignment_bytes_ += alignment_bytes_; |
| stat->object_header_bytes_ += Instructions::HeaderSize(); |
| |
| if (returns_constant) stat->return_const_count_++; |
| if (returns_const_with_load_field_) { |
| stat->return_const_with_load_field_count_++; |
| } |
| } |
| |
| } // namespace dart |